From 66663c84a691b8cb9984f55a1ed0afe58943763a Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Mon, 20 Oct 2025 10:17:52 -0300 Subject: [PATCH 1/7] feat(config): add PHP and PHPMD configuration files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .config/php/error-reporting.ini → custom error reporting - .config/php/xdebug.ini → Xdebug settings - .config/phpmd/ruleset.xml → PHPMD ruleset --- .config/php/error-reporting.ini | 173 ++++++++++++++++++++ .config/php/xdebug.ini | 160 +++++++++++++++++++ .config/phpmd/ruleset.xml | 273 ++++++++++++++++++++++++++++++++ 3 files changed, 606 insertions(+) create mode 100644 .config/php/error-reporting.ini create mode 100644 .config/php/xdebug.ini create mode 100644 .config/phpmd/ruleset.xml diff --git a/.config/php/error-reporting.ini b/.config/php/error-reporting.ini new file mode 100644 index 0000000..5b9469d --- /dev/null +++ b/.config/php/error-reporting.ini @@ -0,0 +1,173 @@ +; ============================================================================ +; KaririCode DevKit - Error Reporting Configuration +; ============================================================================ +; Strict error reporting configuration for development environment +; All errors, warnings, and notices are displayed to catch issues early +; +; Location: devkit/.config/php/error-reporting.ini +; ============================================================================ + +[PHP] +; ============================================================================ +; ERROR REPORTING +; ============================================================================ +; Report all errors, warnings, and notices +error_reporting = E_ALL + +; Display errors on screen (development only) +display_errors = On +display_startup_errors = On + +; Log errors to file +log_errors = On +error_log = /var/log/php_errors.log + +; Detailed error messages +html_errors = On +docref_root = "https://www.php.net/manual/en/" +docref_ext = .html + +; Track all errors +track_errors = Off +xmlrpc_errors = Off + +; ============================================================================ +; ASSERTIONS +; ============================================================================ +; Enable assertions for development +zend.assertions = 1 +assert.active = 1 +assert.exception = 1 +assert.warning = 0 +assert.bail = 0 + +; ============================================================================ +; DEVELOPMENT SETTINGS +; ============================================================================ +; Hide PHP version in headers (security) +expose_php = Off + +; Variables order +variables_order = "EGPCS" +request_order = "GP" + +; Auto-detect line endings +auto_detect_line_endings = Off + +; ============================================================================ +; RESOURCE LIMITS +; ============================================================================ +; Memory limit (generous for development) +memory_limit = 512M + +; Maximum execution time +max_execution_time = 30 +max_input_time = 60 + +; Input size limits +post_max_size = 25M +upload_max_filesize = 20M +max_file_uploads = 20 + +; ============================================================================ +; OUTPUT BUFFERING +; ============================================================================ +; Output buffering (off for immediate error display) +output_buffering = Off +implicit_flush = On + +; ============================================================================ +; DATE/TIME +; ============================================================================ +; Default timezone +date.timezone = UTC + +; ============================================================================ +; SESSION +; ============================================================================ +; Session configuration +session.save_handler = files +session.save_path = "/tmp" +session.use_strict_mode = 1 +session.use_cookies = 1 +session.use_only_cookies = 1 +session.cookie_httponly = 1 +session.cookie_secure = 0 +session.cookie_samesite = "Lax" +session.gc_probability = 1 +session.gc_divisor = 100 +session.gc_maxlifetime = 1440 +session.sid_length = 48 +session.sid_bits_per_character = 6 + +; ============================================================================ +; OPCACHE (disabled for development) +; ============================================================================ +; Disable opcache for development to see code changes immediately +opcache.enable = 0 +opcache.enable_cli = 0 + +; If enabled, use these settings for development: +; opcache.validate_timestamps = 1 +; opcache.revalidate_freq = 0 +; opcache.max_accelerated_files = 10000 +; opcache.memory_consumption = 128 +; opcache.interned_strings_buffer = 16 +; opcache.fast_shutdown = 1 + +; ============================================================================ +; REALPATH CACHE +; ============================================================================ +; Realpath cache (keep small for development) +realpath_cache_size = 4096K +realpath_cache_ttl = 120 + +; ============================================================================ +; FILE UPLOADS +; ============================================================================ +; File uploads enabled +file_uploads = On +upload_tmp_dir = /tmp + +; ============================================================================ +; SECURITY +; ============================================================================ +; Disable dangerous functions (customize as needed) +disable_functions = +disable_classes = + +; ============================================================================ +; MAIL +; ============================================================================ +; Mail configuration (usually handled by application) +SMTP = localhost +smtp_port = 25 +sendmail_path = /usr/sbin/sendmail -t -i + +; ============================================================================ +; MISC +; ============================================================================ +; Allow URL fopen (needed for many libraries) +allow_url_fopen = On +allow_url_include = Off + +; Auto-prepend/append files +auto_prepend_file = +auto_append_file = + +; Default charset +default_charset = "UTF-8" + +; Maximum input variables (prevent resource exhaustion) +max_input_vars = 1000 +max_input_nesting_level = 64 + +; ============================================================================ +; EXTENSIONS +; ============================================================================ +; Common extensions are enabled by default in kariricode/php-api-stack +; Additional extensions can be enabled as needed: +; extension=redis.so +; extension=memcached.so +; extension=apcu.so +; extension=imagick.so \ No newline at end of file diff --git a/.config/php/xdebug.ini b/.config/php/xdebug.ini new file mode 100644 index 0000000..462643e --- /dev/null +++ b/.config/php/xdebug.ini @@ -0,0 +1,160 @@ +; ============================================================================ +; KaririCode DevKit - Xdebug Configuration +; ============================================================================ +; Xdebug 3.x configuration for step debugging and code coverage +; https://xdebug.org/docs/all_settings +; +; Location: devkit/.config/php/xdebug.ini +; ============================================================================ + +[xdebug] +; ============================================================================ +; MODE CONFIGURATION +; ============================================================================ +; Modes: off, develop, coverage, debug, gcstats, profile, trace +; Multiple modes can be combined with commas (e.g., "debug,coverage") +; This is controlled by environment variable XDEBUG_MODE in .env +xdebug.mode=${XDEBUG_MODE} + +; ============================================================================ +; DEBUGGING +; ============================================================================ +; Start debugging automatically or wait for trigger +; Values: yes, no, trigger +xdebug.start_with_request=yes + +; IDE/Client connection settings +; Use host.docker.internal for Docker Desktop (Mac/Windows) +; Use 172.17.0.1 for Docker on Linux +xdebug.client_host=host.docker.internal +xdebug.client_port=9003 + +; Discovery mode for cloud/dynamic environments +; Set to 1 if you need automatic discovery (not recommended for local dev) +xdebug.discover_client_host=0 + +; IDE key for identifying debugging session +; PHPStorm: PHPSTORM +; VSCode: VSCODE +xdebug.idekey=PHPSTORM + +; Connection timeout in milliseconds +xdebug.connect_timeout_ms=2000 + +; ============================================================================ +; LOGGING +; ============================================================================ +; Log file location (useful for debugging connection issues) +xdebug.log=/var/log/xdebug.log + +; Log level (0-10, where 10 is most verbose) +; 0 = Criticals +; 1 = Errors +; 3 = Warnings +; 5 = Communication +; 7 = Information +; 10 = Debug +xdebug.log_level=7 + +; ============================================================================ +; STEP DEBUGGING +; ============================================================================ +; Maximum nesting level for recursive debugging +; Increase if you have deeply nested structures +xdebug.max_nesting_level=512 + +; ============================================================================ +; COVERAGE +; ============================================================================ +; Enable code coverage (required for PHPUnit coverage) +xdebug.coverage_enable=1 + +; ============================================================================ +; DEVELOPMENT MODE +; ============================================================================ +; Development helpers (when mode=develop) +; Show local variables in stack traces +xdebug.dump.GET=* +xdebug.dump.POST=* +xdebug.dump.COOKIE=* +xdebug.dump.FILES=* +xdebug.dump.SESSION=* + +; ============================================================================ +; PROFILING (disabled by default) +; ============================================================================ +; Uncomment to enable profiling +; xdebug.profiler_enable=0 +; xdebug.profiler_enable_trigger=1 +; xdebug.profiler_enable_trigger_value="" +; xdebug.profiler_output_dir=/var/www/profiler +; xdebug.profiler_output_name=cachegrind.out.%p + +; ============================================================================ +; TRACING (disabled by default) +; ============================================================================ +; Uncomment to enable function tracing +; xdebug.trace_enable_trigger=1 +; xdebug.trace_enable_trigger_value="" +; xdebug.trace_output_dir=/var/www/traces +; xdebug.trace_output_name=trace.%c +; xdebug.trace_format=0 +; xdebug.trace_options=0 + +; ============================================================================ +; PERFORMANCE +; ============================================================================ +; Show memory usage in stack traces +xdebug.show_mem_delta=1 + +; ============================================================================ +; DISPLAY +; ============================================================================ +; HTML error output formatting +xdebug.cli_color=1 + +; Variable display depth +xdebug.var_display_max_depth=10 + +; Maximum number of array children/object properties +xdebug.var_display_max_children=256 + +; Maximum string length +xdebug.var_display_max_data=4096 + +; ============================================================================ +; USAGE TIPS +; ============================================================================ +; +; Enable Xdebug: +; make xdebug-on +; +; Disable Xdebug: +; make xdebug-off +; +; Check Status: +; make xdebug-status +; +; IDE Configuration: +; PHPStorm: +; - Settings > PHP > Debug +; - Port: 9003 +; - Check "Accept external connections" +; - Settings > PHP > Servers +; - Add server: localhost, port 9003 +; - Map: /var/www -> your-project-path +; +; VSCode: +; - Install PHP Debug extension +; - Add to launch.json: +; { +; "name": "Listen for Xdebug", +; "type": "php", +; "request": "launch", +; "port": 9003, +; "pathMappings": { +; "/var/www": "${workspaceFolder}" +; } +; } +; +; ============================================================================ \ No newline at end of file diff --git a/.config/phpmd/ruleset.xml b/.config/phpmd/ruleset.xml new file mode 100644 index 0000000..0c05d4d --- /dev/null +++ b/.config/phpmd/ruleset.xml @@ -0,0 +1,273 @@ + + + + + + Professional PHPMD ruleset for KaririCode Framework components. + Enforces clean code principles, SOLID design, and maintainability. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */tests/* + */Tests/* + + + */vendor/* + + + */cache/* + */storage/* + */temp/* + */tmp/* + + + */build/* + */coverage/* + */docs/* + + + */migrations/* + */database/factories/* + */database/seeders/* + + + */_ide_helper*.php + *.blade.php + + + */config/* + + + */public/* + + + + \ No newline at end of file From 2d238e2eaaa601527daf9503b5e0b8b666e3ef7a Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Mon, 20 Oct 2025 10:25:14 -0300 Subject: [PATCH 2/7] ci(github): introduce workflows for CI and code quality checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .github/workflows/ci.yml → automated build and test pipeline - .github/workflows/code-quality.yml → linting, static analysis, and style validation --- .github/workflows/ci.yml | 280 +++++++++++++++++++++ .github/workflows/code-quality.yml | 377 +++++++++++++++++++++++++++++ 2 files changed, 657 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/code-quality.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ebb5cec --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,280 @@ +name: CI Pipeline + +on: + push: + branches: + - main + - develop + - feature/* + pull_request: + branches: + - main + - develop + schedule: + # Run daily at 2 AM UTC + - cron: "0 2 * * *" + +jobs: + # ============================================================================ + # TEST JOB - Run tests across multiple PHP versions + # ============================================================================ + test: + name: Tests (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + permissions: + # Required for codecov/codecov-action to get an OIDC token. + id-token: write + # Required for actions/checkout to fetch code. + contents: read + + strategy: + fail-fast: false + matrix: + php: + - "8.4" + - "8.3" + include: + - php: "8.4" + coverage: true + + services: + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + memcached: + image: memcached:1.6-alpine + ports: + - 11211:11211 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: redis, memcached, mbstring, xml, ctype, json, tokenizer, opcache + coverage: xdebug + tools: composer:v2 + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Validate composer.json and composer.lock + run: composer validate --strict --no-check-lock + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction + + - name: Check PHP syntax + run: find src tests -name "*.php" -print0 | xargs -0 -n1 php -l + + - name: Run tests + if: matrix.coverage != true + run: vendor/bin/phpunit --no-coverage + env: + REDIS_HOST: localhost + REDIS_PORT: 6379 + MEMCACHED_HOST: localhost + MEMCACHED_PORT: 11211 + + - name: Run tests with coverage + if: matrix.coverage == true + run: vendor/bin/phpunit --coverage-clover=coverage.xml --coverage-text + env: + XDEBUG_MODE: coverage + REDIS_HOST: localhost + REDIS_PORT: 6379 + MEMCACHED_HOST: localhost + MEMCACHED_PORT: 11211 + + - name: Upload coverage to Codecov + if: matrix.coverage == true + uses: codecov/codecov-action@v4 + with: + files: ./coverage.xml + flags: unittests + name: codecov-php-${{ matrix.php }} + fail_ci_if_error: false + + - name: Archive code coverage results + if: matrix.coverage == true + uses: actions/upload-artifact@v4 + with: + name: code-coverage-report + path: coverage.xml + retention-days: 30 + + # ============================================================================ + # CODE QUALITY JOB - Run all quality checks + # ============================================================================ + code-quality: + name: Code Quality + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml, ctype, json, tokenizer + coverage: none + tools: composer:v2, cs2pr + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress --no-interaction + + - name: Check code style (PHP CS Fixer) + run: vendor/bin/php-cs-fixer fix --dry-run --diff --format=checkstyle | cs2pr + continue-on-error: true + + - name: Run static analysis (PHPStan) + run: vendor/bin/phpstan analyse --error-format=github --no-progress + + - name: Run mess detector (PHPMD) + run: vendor/bin/phpmd src github devkit/.config/phpmd/ruleset.xml + continue-on-error: true + + - name: Check for security vulnerabilities + run: composer audit --format=plain + + # ============================================================================ + # MUTATION TESTING JOB (Optional - runs on schedule) + # ============================================================================ + mutation: + name: Mutation Testing + runs-on: ubuntu-latest + if: github.event_name == 'schedule' || contains(github.event.head_commit.message, '[mutation]') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml, ctype, json + coverage: xdebug + tools: composer:v2 + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run Infection (Mutation Testing) + run: | + composer require --dev infection/infection + vendor/bin/infection --threads=4 --min-msi=70 --min-covered-msi=80 + continue-on-error: true + + # ============================================================================ + # DOCUMENTATION JOB + # ============================================================================ + documentation: + name: Documentation + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + tools: composer:v2 + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Generate API documentation + run: | + composer require --dev phpdocumentor/phpdocumentor + vendor/bin/phpdoc -d src -t docs/api + continue-on-error: true + + - name: Deploy documentation to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + if: success() + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/api + publish_branch: gh-pages + + # ============================================================================ + # COMPATIBILITY CHECK JOB + # ============================================================================ + compatibility: + name: Backward Compatibility Check + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout current code + uses: actions/checkout@v4 + with: + path: current + + - name: Checkout base code + uses: actions/checkout@v4 + with: + ref: ${{ github.base_ref }} + path: base + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + tools: composer:v2 + + - name: Install roave/backward-compatibility-check + run: | + cd current + composer require --dev roave/backward-compatibility-check + + - name: Check backward compatibility + run: | + cd current + vendor/bin/roave-backward-compatibility-check --from=../base + continue-on-error: true diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 0000000..459e034 --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,377 @@ +name: Code Quality + +on: + push: + branches: + - main + - develop + - feature/* + pull_request: + branches: + - main + - develop + workflow_dispatch: + +jobs: + # ============================================================================ + # PHP CS FIXER - Code Style Check + # ============================================================================ + php-cs-fixer: + name: PHP CS Fixer + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml + coverage: none + tools: composer:v2, cs2pr + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHP CS Fixer + run: | + vendor/bin/php-cs-fixer fix --dry-run --diff --format=checkstyle | cs2pr + + - name: Annotate with PHP CS Fixer results + if: failure() + run: | + echo "::error::Code style issues found. Run 'make cs-fix' to fix them." + + # ============================================================================ + # PHPSTAN - Static Analysis + # ============================================================================ + phpstan: + name: PHPStan (Level Max) + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml, ctype, json + coverage: none + tools: composer:v2 + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHPStan + run: vendor/bin/phpstan analyse --error-format=github --no-progress + + - name: Generate PHPStan baseline (if needed) + if: failure() + run: | + vendor/bin/phpstan analyse --generate-baseline + echo "::warning::PHPStan baseline generated. Consider fixing issues instead of ignoring them." + continue-on-error: true + + # ============================================================================ + # PHPMD - Mess Detector + # ============================================================================ + phpmd: + name: PHP Mess Detector + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml + coverage: none + tools: composer:v2 + + - name: Get Composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run PHPMD + run: vendor/bin/phpmd src github devkit/.config/phpmd/ruleset.xml + continue-on-error: true + + - name: Upload PHPMD results + if: always() + uses: actions/upload-artifact@v4 + with: + name: phpmd-results + path: phpmd-report.xml + continue-on-error: true + + # ============================================================================ + # RECTOR - Automated Refactoring Check + # ============================================================================ + rector: + name: Rector Dry Run + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml, ctype, json + coverage: none + tools: composer:v2 + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run Rector (dry-run) + run: vendor/bin/rector process --dry-run --no-progress-bar + continue-on-error: true + + - name: Suggest improvements + if: failure() + run: | + echo "::warning::Rector found potential improvements. Run 'make rector-fix' to apply them." + + # ============================================================================ + # SECURITY AUDIT - Composer Security Check + # ============================================================================ + security: + name: Security Audit + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + tools: composer:v2 + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run security audit + run: composer audit --format=json > security-report.json + continue-on-error: true + + - name: Check for vulnerabilities + run: | + VULNS=$(jq '.advisories | length' security-report.json) + if [ "$VULNS" -gt 0 ]; then + echo "::error::Found $VULNS security vulnerabilities" + cat security-report.json + exit 1 + fi + echo "::notice::No security vulnerabilities found" + + - name: Upload security report + if: always() + uses: actions/upload-artifact@v4 + with: + name: security-report + path: security-report.json + + # ============================================================================ + # PSALM - Static Analysis (Alternative) + # ============================================================================ + psalm: + name: Psalm Static Analysis + runs-on: ubuntu-latest + if: contains(github.event.head_commit.message, '[psalm]') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: mbstring, xml + coverage: none + tools: composer:v2 + + - name: Install dependencies + run: | + composer install --prefer-dist --no-progress + composer require --dev vimeo/psalm + + - name: Run Psalm + run: vendor/bin/psalm --output-format=github --no-progress + continue-on-error: true + + # ============================================================================ + # CODE METRICS - PHPMetrics + # ============================================================================ + metrics: + name: Code Metrics + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || contains(github.event.head_commit.message, '[metrics]') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + tools: composer:v2 + + - name: Install dependencies + run: | + composer install --prefer-dist --no-progress + composer require --dev phpmetrics/phpmetrics + + - name: Generate metrics + run: vendor/bin/phpmetrics --report-html=metrics src + + - name: Upload metrics + uses: actions/upload-artifact@v4 + with: + name: code-metrics + path: metrics/ + retention-days: 30 + + # ============================================================================ + # DEAD CODE DETECTION + # ============================================================================ + dead-code: + name: Dead Code Detection + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + tools: composer:v2 + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Detect dead code (via PHPStan) + run: | + composer require --dev phpstan/phpstan-deprecation-rules + vendor/bin/phpstan analyse src --level=max + continue-on-error: true + + # ============================================================================ + # DEPENDENCY VALIDATION + # ============================================================================ + dependencies: + name: Dependency Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + tools: composer:v2 + + - name: Validate composer.json + run: composer validate --strict --no-check-lock + + - name: Check for outdated dependencies + run: composer outdated --direct --strict + continue-on-error: true + + - name: Check platform requirements + run: composer check-platform-reqs + + # ============================================================================ + # FINAL REPORT - Quality Summary + # ============================================================================ + quality-summary: + name: Quality Summary + runs-on: ubuntu-latest + needs: [php-cs-fixer, phpstan, phpmd, security, dependencies] + if: always() + + steps: + - name: Check overall quality status + run: | + echo "## Quality Checks Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY + echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| PHP CS Fixer | ${{ needs.php-cs-fixer.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| PHPStan | ${{ needs.phpstan.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| PHPMD | ${{ needs.phpmd.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Security | ${{ needs.security.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Dependencies | ${{ needs.dependencies.result }} |" >> $GITHUB_STEP_SUMMARY + + if [ "${{ needs.php-cs-fixer.result }}" != "success" ] || \ + [ "${{ needs.phpstan.result }}" != "success" ] || \ + [ "${{ needs.security.result }}" != "success" ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "❌ Quality checks failed. Please review the logs above." >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ All quality checks passed!" >> $GITHUB_STEP_SUMMARY + + - name: Comment on PR + if: github.event_name == 'pull_request' && failure() + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '⚠️ **Quality checks failed**\n\nPlease run `make qa` locally to fix issues before merging.' + }) From d54c84bf546c5cf98973329a73c43d7c162c57ef Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 22 Oct 2025 11:13:00 -0300 Subject: [PATCH 3/7] feat(devkit): initialize unified PHP 8.4 DevKit scaffold with Docker Compose & Makefile - Add docker-compose.yml using kariricode/php-api-stack as the single base image - Introduce professional Makefile with common dev/QA targets - Add install.sh bootstrapper (interactive composer.json + skeleton generation) - Provide quality tools config: PHP-CS-Fixer (.php-cs-fixer.php), PHPStan (phpstan.neon), PHPUnit (phpunit.xml), Rector (rector.php), PHPMD rules (devkit/.config/phpmd/ruleset.xml) - Include runtime configs: error-reporting.ini, xdebug.ini, opcache.ini (under devkit/.config/php/) - Add project metadata and templates: README.md, .env.example, .gitattributes, .gitignore - Add base composer.json for components --- .config/php/error-reporting.ini | 15 - .env.example | 54 +++ .gitattributes | 176 +++++++ .gitignore | 209 ++++++++ .php-cs-fixer.php | 307 ++++++++++++ Makefile | 324 +++++++++++++ README.md | 640 +++++++++++++++++++++++++ composer.json | 65 +++ devkit/.config/php/error-reporting.ini | 145 ++++++ devkit/.config/php/opcache.ini | 15 + devkit/.config/php/xdebug.ini | 160 +++++++ devkit/.config/phpmd/ruleset.xml | 273 +++++++++++ docker-compose.yml | 54 +++ install.sh | 638 ++++++++++++++++++++++++ phpstan.neon | 129 +++++ phpunit.xml | 0 rector.php | 154 ++++++ 17 files changed, 3343 insertions(+), 15 deletions(-) create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .php-cs-fixer.php create mode 100644 Makefile create mode 100644 README.md create mode 100644 composer.json create mode 100644 devkit/.config/php/error-reporting.ini create mode 100644 devkit/.config/php/opcache.ini create mode 100644 devkit/.config/php/xdebug.ini create mode 100644 devkit/.config/phpmd/ruleset.xml create mode 100644 docker-compose.yml create mode 100644 install.sh create mode 100644 phpstan.neon create mode 100644 phpunit.xml create mode 100644 rector.php diff --git a/.config/php/error-reporting.ini b/.config/php/error-reporting.ini index 5b9469d..0d86f39 100644 --- a/.config/php/error-reporting.ini +++ b/.config/php/error-reporting.ini @@ -100,21 +100,6 @@ session.gc_maxlifetime = 1440 session.sid_length = 48 session.sid_bits_per_character = 6 -; ============================================================================ -; OPCACHE (disabled for development) -; ============================================================================ -; Disable opcache for development to see code changes immediately -opcache.enable = 0 -opcache.enable_cli = 0 - -; If enabled, use these settings for development: -; opcache.validate_timestamps = 1 -; opcache.revalidate_freq = 0 -; opcache.max_accelerated_files = 10000 -; opcache.memory_consumption = 128 -; opcache.interned_strings_buffer = 16 -; opcache.fast_shutdown = 1 - ; ============================================================================ ; REALPATH CACHE ; ============================================================================ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..80cb40e --- /dev/null +++ b/.env.example @@ -0,0 +1,54 @@ +# ============================================================================ +# KaririCode DevKit - Environment Configuration +# ============================================================================ +# IMPORTANTE: Copie este arquivo para .env e ajuste conforme necessário +# cp .env.example .env +# ============================================================================ + +# ============================================================================ +# PROJECT CONFIGURATION +# ============================================================================ +APP_NAME=kariricode-devkit +APP_ENV=development +APP_DEBUG=true +APP_PORT=8089 + +# Health Check Installation (optional) +# Set to 'true' to install health check endpoint +# Automatically enabled when DEMO_MODE=true +DEMO_MODE=false +HEALTH_CHECK_INSTALL=true + +# ============================================================================ +# PORTS - Ajuste se houver conflito com serviços existentes +# Usually 6379 for Redis and 11211 for Memcached +# ============================================================================ +REDIS_PORT=63777 +MEMCACHED_PORT=11210 + + +# ============================================================================ +# XDEBUG - Descomente para habilitar +# ============================================================================ +XDEBUG_MODE=off +XDEBUG_CLIENT_HOST=host.docker.internal +COMPOSER_MEMORY_LIMIT=-1 + +# ============================================================================ +# REDIS +# ============================================================================ +REDIS_MAXMEMORY=256mb +REDIS_MAXMEMORY_POLICY=allkeys-lru +REDIS_APPENDONLY=yes + +# ============================================================================ +# MEMCACHED +# ============================================================================ +MEMCACHED_MEMORY=256 + +# ============================================================================ +# RESOLUÇÃO DE CONFLITOS: +# 1. Identifique a porta em uso: sudo lsof -i :6379 +# 2. Altere REDIS_PORT para 6380 ou outra porta disponível +# 3. Execute: make down && make up +# ============================================================================ \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4f2f076 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,176 @@ +# KaririCode DevKit - Git Attributes Configuration +# Ensures consistent line endings and export-ignore for distribution + +# Auto detect text files and normalize line endings to LF +* text=auto eol=lf + +# ============================================================================ +# SOURCE CODE +# ============================================================================ +*.php text eol=lf diff=php +*.inc text eol=lf +*.module text eol=lf +*.install text eol=lf +*.test text eol=lf +*.theme text eol=lf + +# ============================================================================ +# CONFIGURATION & DATA +# ============================================================================ +*.json text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.xml text eol=lf +*.ini text eol=lf +*.toml text eol=lf +*.conf text eol=lf +*.config text eol=lf +.env* text eol=lf +*.lock text eol=lf -diff + +# ============================================================================ +# DOCUMENTATION +# ============================================================================ +*.md text eol=lf diff=markdown +*.txt text eol=lf +*.rst text eol=lf +AUTHORS text eol=lf +CHANGELOG text eol=lf +CHANGES text eol=lf +CONTRIBUTING text eol=lf +COPYING text eol=lf +copyright text eol=lf +*COPYRIGHT* text eol=lf +INSTALL text eol=lf +license text eol=lf +LICENSE text eol=lf +NEWS text eol=lf +readme text eol=lf +*README* text eol=lf +TODO text eol=lf + +# ============================================================================ +# SCRIPTS +# ============================================================================ +*.sh text eol=lf +*.bash text eol=lf +*.zsh text eol=lf +*.fish text eol=lf +Makefile text eol=lf +makefile text eol=lf +*.mk text eol=lf + +# ============================================================================ +# DOCKER +# ============================================================================ +Dockerfile text eol=lf +docker-compose*.yml text eol=lf +.dockerignore text eol=lf + +# ============================================================================ +# WEB FILES +# ============================================================================ +*.html text eol=lf diff=html +*.htm text eol=lf diff=html +*.css text eol=lf diff=css +*.scss text eol=lf +*.sass text eol=lf +*.less text eol=lf +*.js text eol=lf +*.mjs text eol=lf +*.ts text eol=lf +*.jsx text eol=lf +*.tsx text eol=lf +*.vue text eol=lf + +# ============================================================================ +# BINARY FILES +# ============================================================================ +# Images +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.svg binary +*.webp binary +*.bmp binary +*.tiff binary + +# Fonts +*.ttf binary +*.otf binary +*.woff binary +*.woff2 binary +*.eot binary + +# Archives +*.zip binary +*.tar binary +*.gz binary +*.bz2 binary +*.7z binary +*.rar binary +*.phar binary + +# Executables +*.exe binary +*.dll binary +*.so binary +*.dylib binary + +# Other +*.pdf binary +*.psd binary +*.ai binary + +# ============================================================================ +# EXPORT-IGNORE (files not included in distribution archives) +# ============================================================================ +# Development files +/.github export-ignore +/tests export-ignore +/docs export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.php export-ignore +/.php-cs-fixer.cache export-ignore +/phpstan.neon export-ignore +/phpstan-baseline.neon export-ignore +/phpunit.xml export-ignore +/phpunit.xml.dist export-ignore +/.phpunit.cache export-ignore +/rector.php export-ignore +/.rector_cache export-ignore +/devkit export-ignore +/docker-compose.yml export-ignore +/Makefile export-ignore +/.env* export-ignore +/coverage export-ignore +/build export-ignore +/.idea export-ignore +/.vscode export-ignore +/install.sh export-ignore + +# CI/CD files +/.github export-ignore +/.gitlab-ci.yml export-ignore +/.travis.yml export-ignore +/azure-pipelines.yml export-ignore +/.circleci export-ignore + +# Documentation files +/README.md export-ignore +/CHANGELOG.md export-ignore +/CONTRIBUTING.md export-ignore +/CODE_OF_CONDUCT.md export-ignore +/SECURITY.md export-ignore +/UPGRADING.md export-ignore + +# PHP specific +/composer.lock export-ignore + +# Git files +/.gitattributes export-ignore +/.gitignore export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d65403b --- /dev/null +++ b/.gitignore @@ -0,0 +1,209 @@ +# ============================================================================ +# KaririCode DevKit - Git Ignore Configuration +# ============================================================================ +# Comprehensive ignore rules for development environment +# ============================================================================ + +# ============================================================================ +# VENDOR & DEPENDENCIES +# ============================================================================ +/vendor/ +/composer.lock +composer.phar +/node_modules/ +package-lock.json +yarn.lock + +# ============================================================================ +# ENVIRONMENT & SECRETS +# ============================================================================ +.env +.env.local +.env.*.local +.env.backup +.env.xdebug +*.key +*.pem + +# ============================================================================ +# IDE & EDITORS +# ============================================================================ +# PHPStorm / IntelliJ +.idea/ +*.iml +*.iws +*.ipr + +# VSCode +.vscode/ +*.code-workspace + +# Sublime Text +*.sublime-project +*.sublime-workspace + +# Vim +*.swp +*.swo +*~ + +# Emacs +*~ +\#*\# +.\#* + +# NetBeans +nbproject/ + +# ============================================================================ +# OPERATING SYSTEM FILES +# ============================================================================ +# macOS +.DS_Store +.AppleDouble +.LSOverride +._* +.Spotlight-V100 +.Trashes + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ +*.lnk + +# Linux +.directory +.Trash-* + +# ============================================================================ +# PHP TESTING & COVERAGE +# ============================================================================ +/.phpunit.cache/ +/coverage/ +/build/ +/.phpunit.result.cache +/test-results/ + +# ============================================================================ +# QUALITY TOOLS CACHE +# ============================================================================ +.php-cs-fixer.cache +/.php-cs-fixer.cache +.phpstan.cache +/.phpstan.cache +/.rector_cache/ +/.psalm_cache/ +/metrics/ + +# ============================================================================ +# LOGS & TEMPORARY FILES +# ============================================================================ +*.log +*.tmp +*.temp +*.cache +*.bak +*.old +*.orig +*.swp +/logs/ +/tmp/ + +# ============================================================================ +# DOCKER +# ============================================================================ +/docker-compose.override.yml +/.docker/ +!docker/ + +# ============================================================================ +# PUBLIC DIRECTORY +# ============================================================================ +# Created by php-api-stack image for demo/testing +/public/ + +# ============================================================================ +# COMPONENT DEVELOPMENT +# ============================================================================ +# When developing specific components, these are component-specific +# DevKit base doesn't have src/tests, but components created will +/src/ +/tests/Unit/ +/tests/Integration/ +/tests/Fixtures/ + +# Keep base test structure +!/tests/ +!/tests/.gitkeep + +# ============================================================================ +# DOCUMENTATION BUILD +# ============================================================================ +/docs/_build/ +/docs/.phpdoc/ +/.phpdoc/ +/phpdoc/ + +# ============================================================================ +# ARCHIVES & DISTRIBUTIONS +# ============================================================================ +*.zip +*.tar +*.tar.gz +*.rar +*.7z +*.phar + +# ============================================================================ +# GENERATED FILES +# ============================================================================ +/dist/ +/output/ +/generated/ + +# ============================================================================ +# SECURITY SCAN RESULTS +# ============================================================================ +/security-checker.cache +/trivy-results.json +/snyk-results.json + +# ============================================================================ +# CI/CD ARTIFACTS +# ============================================================================ +/.github/workflows/*-local.yml +/.gitlab-ci-local.yml + +# ============================================================================ +# PERFORMANCE & PROFILING +# ============================================================================ +*.cachegrind +*.xhprof +/profiler/ +/blackfire/ + +# ============================================================================ +# DATABASE +# ============================================================================ +*.sqlite +*.sqlite3 +*.db + +# ============================================================================ +# SESSION & CACHE +# ============================================================================ +/storage/framework/cache/* +/storage/framework/sessions/* +/storage/framework/views/* +/storage/logs/* + +# ============================================================================ +# MISC +# ============================================================================ +auth.json +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..f8904d9 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,307 @@ +in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new Config()) + ->setRiskyAllowed(true) + ->setRules([ + // PSR Standards + '@PSR12' => true, + '@PSR12:risky' => true, + + // Strict Types + 'declare_strict_types' => true, + 'strict_param' => true, + 'strict_comparison' => true, + + // Array Notation + 'array_syntax' => ['syntax' => 'short'], + 'no_multiline_whitespace_around_double_arrow' => true, + 'trim_array_spaces' => true, + 'whitespace_after_comma_in_array' => true, + 'array_indentation' => true, + + // Binary Operators + 'binary_operator_spaces' => [ + 'default' => 'single_space', + 'operators' => ['=>' => 'align_single_space_minimal'], + ], + 'concat_space' => ['spacing' => 'one'], + + // Blank Lines + 'blank_line_after_namespace' => true, + 'blank_line_after_opening_tag' => true, + 'blank_line_before_statement' => [ + 'statements' => ['return', 'try', 'throw', 'declare'], + ], + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'extra', + 'throw', + 'use', + 'curly_brace_block', + ], + ], + + // Casing + 'constant_case' => ['case' => 'lower'], + 'lowercase_keywords' => true, + 'lowercase_static_reference' => true, + 'magic_constant_casing' => true, + 'magic_method_casing' => true, + 'native_function_casing' => true, + 'native_function_type_declaration_casing' => true, + + // Cast Notation + 'cast_spaces' => ['space' => 'single'], + 'lowercase_cast' => true, + 'modernize_types_casting' => true, + 'no_short_bool_cast' => true, + 'no_unset_cast' => true, + + // Class Notation + 'class_attributes_separation' => [ + 'elements' => [ + 'const' => 'one', + 'method' => 'one', + 'property' => 'one', + 'trait_import' => 'none', + ], + ], + 'class_definition' => [ + 'multi_line_extends_each_single_line' => true, + 'single_item_single_line' => true, + 'single_line' => true, + ], + 'final_class' => false, // Allow non-final classes for framework components + 'final_internal_class' => false, + 'no_blank_lines_after_class_opening' => true, + 'no_null_property_initialization' => true, + 'ordered_class_elements' => [ + 'order' => [ + 'use_trait', + 'constant_public', + 'constant_protected', + 'constant_private', + 'property_public', + 'property_protected', + 'property_private', + 'construct', + 'destruct', + 'magic', + 'phpunit', + 'method_public', + 'method_protected', + 'method_private', + ], + 'sort_algorithm' => 'none', + ], + 'ordered_interfaces' => ['order' => 'alpha'], + 'protected_to_private' => true, + 'self_accessor' => true, + 'self_static_accessor' => true, + 'single_class_element_per_statement' => true, + 'visibility_required' => [ + 'elements' => ['const', 'method', 'property'], + ], + + // Comments + 'comment_to_phpdoc' => true, + 'multiline_comment_opening_closing' => true, + 'no_empty_comment' => true, + 'single_line_comment_style' => ['comment_types' => ['hash']], + + // Control Structures + 'control_structure_braces' => true, + 'control_structure_continuation_position' => ['position' => 'same_line'], + 'elseif' => true, + 'no_alternative_syntax' => true, + 'no_superfluous_elseif' => true, + 'no_trailing_comma_in_singleline' => true, + 'no_useless_else' => true, + 'simplified_if_return' => true, + 'switch_case_semicolon_to_colon' => true, + 'switch_case_space' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arrays', 'arguments', 'parameters']], + + // Doctrine Annotations + 'doctrine_annotation_array_assignment' => false, + 'doctrine_annotation_braces' => false, + 'doctrine_annotation_indentation' => false, + 'doctrine_annotation_spaces' => false, + + // Function Notation + 'function_declaration' => ['closure_function_spacing' => 'one'], + 'function_typehint_space' => true, + 'lambda_not_used_import' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => false, + ], + 'native_function_invocation' => [ + 'include' => ['@compiler_optimized'], + 'scope' => 'namespaced', + ], + 'no_spaces_after_function_name' => true, + 'nullable_type_declaration_for_default_null_value' => true, + 'return_type_declaration' => ['space_before' => 'none'], + 'single_line_throw' => true, + 'void_return' => false, + + // Import Statements + 'fully_qualified_strict_types' => true, + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_constants' => true, + 'import_functions' => true, + ], + 'no_leading_import_slash' => true, + 'no_unneeded_import_alias' => true, + 'no_unused_imports' => true, + 'ordered_imports' => [ + 'sort_algorithm' => 'alpha', + 'imports_order' => ['class', 'function', 'const'], + ], + 'single_import_per_statement' => true, + 'single_line_after_imports' => true, + + // Language Constructs + 'combine_consecutive_issets' => true, + 'combine_consecutive_unsets' => true, + 'declare_equal_normalize' => ['space' => 'none'], + 'dir_constant' => true, + 'explicit_indirect_variable' => true, + 'is_null' => true, + 'modernize_strpos' => true, + 'no_unset_on_property' => false, + + // Namespaces + 'blank_line_after_namespace' => true, + 'no_blank_lines_before_namespace' => false, + 'single_blank_line_before_namespace' => true, + + // Operators + 'increment_style' => ['style' => 'post'], + 'logical_operators' => true, + 'no_space_around_double_colon' => true, + 'not_operator_with_successor_space' => true, + 'object_operator_without_whitespace' => true, + 'operator_linebreak' => ['only_booleans' => true], + 'standardize_not_equals' => true, + 'ternary_operator_spaces' => true, + 'ternary_to_elvis_operator' => true, + 'ternary_to_null_coalescing' => true, + 'unary_operator_spaces' => true, + + // PHPDoc - Configured to preserve comprehensive documentation like KaririCode\Router + 'align_multiline_comment' => ['comment_type' => 'phpdocs_like'], + 'general_phpdoc_annotation_remove' => false, // Keep all annotations including @author + 'no_blank_lines_after_phpdoc' => true, + 'no_empty_phpdoc' => true, + 'no_superfluous_phpdoc_tags' => false, // Keep all PHPDoc tags including type hints + 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], + 'phpdoc_align' => [ + 'align' => 'vertical', + 'tags' => ['param', 'return', 'throws', 'type', 'var', 'method'], + ], + 'phpdoc_annotation_without_dot' => false, // Allow dots in descriptions + 'phpdoc_indent' => true, + 'phpdoc_inline_tag_normalizer' => true, + 'phpdoc_line_span' => [ + 'const' => 'multi', + 'property' => 'multi', + 'method' => 'multi', + ], + 'phpdoc_no_access' => false, // Keep @access tags if present + 'phpdoc_no_alias_tag' => true, + 'phpdoc_no_package' => false, // Keep @package tags + 'phpdoc_no_useless_inheritdoc' => false, // Keep @inheritDoc for clarity + 'phpdoc_order' => ['order' => ['param', 'throws', 'return']], + 'phpdoc_order_by_value' => ['annotations' => ['covers', 'dataProvider']], + 'phpdoc_return_self_reference' => true, + 'phpdoc_scalar' => true, + 'phpdoc_separation' => [ + 'groups' => [ + ['deprecated', 'link', 'see', 'since'], + ['author', 'copyright', 'license'], + ['category', 'package', 'subpackage'], + ['property', 'property-read', 'property-write'], + ['param', 'return'], + ], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_summary' => true, + 'phpdoc_tag_casing' => ['tags' => ['inheritDoc']], + 'phpdoc_tag_type' => ['tags' => ['inheritDoc' => 'inline']], + 'phpdoc_to_comment' => false, // Keep @var, @see, etc as PHPDoc + 'phpdoc_trim' => true, + 'phpdoc_trim_consecutive_blank_line_separation' => true, + 'phpdoc_types' => true, + 'phpdoc_types_order' => [ + 'null_adjustment' => 'always_last', + 'sort_algorithm' => 'none', + ], + 'phpdoc_var_annotation_correct_order' => true, + 'phpdoc_var_without_name' => true, + + // Return Notation + 'no_useless_return' => true, + 'return_assignment' => true, + 'simplified_null_return' => false, + + // Semicolons + 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'], + 'no_empty_statement' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'semicolon_after_instruction' => true, + 'space_after_semicolon' => ['remove_in_empty_for_expressions' => true], + + // Strings + 'explicit_string_variable' => true, + 'heredoc_to_nowdoc' => true, + 'simple_to_complex_string_variable' => true, + 'single_quote' => true, + 'string_line_ending' => true, + + // Whitespace + 'compact_nullable_typehint' => true, + 'heredoc_indentation' => ['indentation' => 'start_plus_one'], + 'indentation_type' => true, + 'line_ending' => true, + 'method_chaining_indentation' => true, + 'no_spaces_around_offset' => true, + 'no_spaces_inside_parenthesis' => true, + 'no_trailing_whitespace' => true, + 'no_trailing_whitespace_in_comment' => true, + 'no_whitespace_in_blank_line' => true, + 'single_blank_line_at_eof' => true, + 'statement_indentation' => true, + 'types_spaces' => ['space' => 'none'], + ]) + ->setFinder($finder) + ->setLineEnding("\n") + ->setUsingCache(true) + ->setCacheFile(__DIR__ . '/.php-cs-fixer.cache'); diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ba8af12 --- /dev/null +++ b/Makefile @@ -0,0 +1,324 @@ +# ============================================================================ +# KaririCode DevKit - Professional Development Environment +# ============================================================================ +# Repository: https://github.com/KaririCode-Framework/kariricode-devkit +# License: MIT +# ============================================================================ + +.DEFAULT_GOAL := help +.PHONY: help + +# Colors for output +BLUE := \033[0;34m +GREEN := \033[0;32m +YELLOW := \033[0;33m +RED := \033[0;31m +NC := \033[0m # No Color + +# Docker Compose command +DOCKER_COMPOSE := docker-compose +EXEC_PHP := $(DOCKER_COMPOSE) exec php +EXEC_PHP_ROOT := $(DOCKER_COMPOSE) exec -u root php + +# ============================================================================ +# HELP +# ============================================================================ + +help: ## Show this help message + @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" + @echo "$(BLUE)║ KaririCode DevKit - Available Commands ║$(NC)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "" + +# ============================================================================ +# DOCKER MANAGEMENT +# ============================================================================: ## Create required directory structure +up: ## Start all containers in detached mode + @echo "$(BLUE)🚀 Starting containers...$(NC)" + @$(DOCKER_COMPOSE) up -d + @echo "$(GREEN)✓ Containers started successfully$(NC)" + +down: ## Stop and remove all containers + @echo "$(YELLOW)🛑 Stopping containers...$(NC)" + @$(DOCKER_COMPOSE) down + @echo "$(GREEN)✓ Containers stopped$(NC)" + +restart: down up ## Restart all containers + +status: ## Show status of all containers + @$(DOCKER_COMPOSE) ps + +logs: ## Show logs from all containers (use CTRL+C to exit) + @$(DOCKER_COMPOSE) logs -f + +logs-php: ## Show PHP container logs + @$(DOCKER_COMPOSE) logs -f php + +logs-redis: ## Show Redis container logs + @$(DOCKER_COMPOSE) logs -f redis + +shell: ## Access PHP container shell as app user + @$(EXEC_PHP) /bin/bash + +shell-root: ## Access PHP container shell as root user + @$(EXEC_PHP_ROOT) /bin/bash + +# ============================================================================ +# DEPENDENCY MANAGEMENT +# ============================================================================ + +install: ## Install composer dependencies + @echo "$(BLUE)📦 Installing dependencies...$(NC)" + @if [ -f composer.json ]; then \ + $(EXEC_PHP) composer install --no-interaction --prefer-dist --optimize-autoloader; \ + echo "$(GREEN)✓ Dependencies installed$(NC)"; \ + else \ + echo "$(YELLOW)⚠ No composer.json found$(NC)"; \ + echo "$(YELLOW) Run './install.sh' to create a new component$(NC)"; \ + fi + +update: ## Update composer dependencies + @echo "$(BLUE)🔄 Updating dependencies...$(NC)" + @$(EXEC_PHP) composer update + @echo "$(GREEN)✓ Dependencies updated$(NC)" + +require: ## Install a new package (use: make require PKG=vendor/package) + @test -n "$(PKG)" || (echo "$(RED)Error: PKG variable is required. Usage: make require PKG=vendor/package$(NC)" && exit 1) + @$(EXEC_PHP) composer require $(PKG) + +require-dev: ## Install a new dev package (use: make require-dev PKG=vendor/package) + @test -n "$(PKG)" || (echo "$(RED)Error: PKG variable is required. Usage: make require-dev PKG=vendor/package$(NC)" && exit 1) + @$(EXEC_PHP) composer require --dev $(PKG) + +autoload: ## Dump composer autoload + @$(EXEC_PHP) composer dump-autoload + +validate: ## Validate composer.json + @$(EXEC_PHP) composer validate --strict + +outdated: ## Show outdated packages + @$(EXEC_PHP) composer outdated --direct + +# ============================================================================ +# TESTING +# ============================================================================ + +test: ## Run all tests + @echo "$(BLUE)🧪 Running tests...$(NC)" + @$(EXEC_PHP) vendor/bin/phpunit + @echo "$(GREEN)✓ Tests completed$(NC)" + +test-coverage: ## Run tests with coverage report (HTML) + @echo "$(BLUE)🧪 Running tests with coverage...$(NC)" + @$(EXEC_PHP) vendor/bin/phpunit --coverage-html=coverage + @echo "$(GREEN)✓ Coverage report generated in ./coverage$(NC)" + +test-coverage-text: ## Run tests with coverage report (terminal) + @$(EXEC_PHP) vendor/bin/phpunit --coverage-text + +test-unit: ## Run unit tests only + @$(EXEC_PHP) vendor/bin/phpunit --testsuite=Unit + +test-integration: ## Run integration tests only + @$(EXEC_PHP) vendor/bin/phpunit --testsuite=Integration + +test-filter: ## Run specific test (use: make test-filter FILTER=TestClassName) + @test -n "$(FILTER)" || (echo "$(RED)Error: FILTER variable is required$(NC)" && exit 1) + @$(EXEC_PHP) vendor/bin/phpunit --filter $(FILTER) + +# ============================================================================ +# CODE QUALITY +# ============================================================================ + +cs-check: ## Check code style (dry-run) + @echo "$(BLUE)🔍 Checking code style...$(NC)" + @$(EXEC_PHP) vendor/bin/php-cs-fixer fix --dry-run --diff --verbose + +cs-fix: ## Fix code style + @echo "$(BLUE)✨ Fixing code style...$(NC)" + @$(EXEC_PHP) vendor/bin/php-cs-fixer fix + @echo "$(GREEN)✓ Code style fixed$(NC)" + +analyse: ## Run static analysis with PHPStan (max level) + @echo "$(BLUE)🔬 Running static analysis...$(NC)" + @$(EXEC_PHP) vendor/bin/phpstan analyse src tests --level=max + @echo "$(GREEN)✓ Static analysis completed$(NC)" + +analyse-baseline: ## Generate PHPStan baseline + @$(EXEC_PHP) vendor/bin/phpstan analyse src tests --level=max --generate-baseline + +phpmd: ## Run PHP Mess Detector + @echo "$(BLUE)🔍 Running PHP Mess Detector...$(NC)" + @$(EXEC_PHP) vendor/bin/phpmd src text devkit/.config/phpmd/ruleset.xml + +rector: ## Run Rector (dry-run) + @echo "$(BLUE)🔧 Running Rector...$(NC)" + @$(EXEC_PHP) vendor/bin/rector process --dry-run + +rector-fix: ## Run Rector and apply changes + @$(EXEC_PHP) vendor/bin/rector process + +# ============================================================================ +# COMPREHENSIVE QUALITY CHECKS +# ============================================================================ + +check: cs-check analyse phpmd ## Run all quality checks (CS, PHPStan, PHPMD) + @echo "$(GREEN)✓ All quality checks completed$(NC)" + +fix: cs-fix ## Fix all auto-fixable issues + +qa: fix test check ## Complete QA pipeline: fix, test, and check + +# ============================================================================ +# SECURITY +# ============================================================================ + +security: ## Check for security vulnerabilities + @echo "$(BLUE)🔒 Checking security vulnerabilities...$(NC)" + @$(EXEC_PHP) composer audit + @echo "$(GREEN)✓ Security check completed$(NC)" + +# ============================================================================ +# CACHE OPERATIONS +# ============================================================================ + +redis-cli: ## Access Redis CLI + @$(DOCKER_COMPOSE) exec redis redis-cli + +redis-flush: ## Flush Redis cache + @echo "$(YELLOW)🗑️ Flushing Redis cache...$(NC)" + @$(DOCKER_COMPOSE) exec redis redis-cli FLUSHALL + @echo "$(GREEN)✓ Redis cache flushed$(NC)" + +redis-info: ## Show Redis information + @$(DOCKER_COMPOSE) exec redis redis-cli INFO + +memcached-stats: ## Show Memcached statistics + @$(DOCKER_COMPOSE) exec memcached sh -c 'echo stats | nc localhost 11211' + +memcached-flush: ## Flush Memcached + @echo "$(YELLOW)🗑️ Flushing Memcached...$(NC)" + @$(DOCKER_COMPOSE) exec memcached sh -c 'echo flush_all | nc localhost 11211' + @echo "$(GREEN)✓ Memcached flushed$(NC)" + +# ============================================================================ +# UTILITIES +# ============================================================================ + +clean: ## Clean all generated files and caches + @echo "$(YELLOW)🧹 Cleaning generated files...$(NC)" + @sudo rm -rf vendor/ + @sudo rm -rf coverage/ + @sudo rm -rf .phpunit.cache/ + @sudo rm -rf .php-cs-fixer.cache + @sudo rm -rf composer.lock + @echo "$(GREEN)✓ Cleanup completed$(NC)" + +reset: clean down ## Complete reset (clean + remove containers) + @echo "$(GREEN)✓ Environment reset completed$(NC)" + +rebuild: reset force-rebuild install ## Rebuild environment from scratch + @echo "$(GREEN)✓ Environment rebuilt$(NC)" + +force-rebuild: ## Force rebuild images (no cache) and recreate containers + @echo "$(YELLOW)🔥 Forcing image rebuild (no cache)...$(NC)" + @$(DOCKER_COMPOSE) build --no-cache + @echo "$(YELLOW)🔥 Recreating containers...$(NC)" + @$(DOCKER_COMPOSE) up --force-recreate -d + @echo "$(GREEN)✓ Rebuild and recreate complete$(NC)" + +permissions: ## Fix file permissions + @echo "$(BLUE)🔧 Fixing permissions...$(NC)" + @$(EXEC_PHP_ROOT) chown -R app:app /var/www/html + @echo "$(GREEN)✓ Permissions fixed$(NC)" + +# ============================================================================ +# XDEBUG +# ============================================================================ + +xdebug-on: ## Enable Xdebug + @echo "$(BLUE)🐛 Enabling Xdebug...$(NC)" + @echo "XDEBUG_MODE=debug,coverage" > .env.xdebug + @$(DOCKER_COMPOSE) restart php + @echo "$(GREEN)✓ Xdebug enabled$(NC)" + +xdebug-off: ## Disable Xdebug + @echo "$(BLUE)🐛 Disabling Xdebug...$(NC)" + @echo "XDEBUG_MODE=off" > .env.xdebug + @$(DOCKER_COMPOSE) restart php + @echo "$(GREEN)✓ Xdebug disabled$(NC)" + +xdebug-status: ## Show Xdebug status + @$(EXEC_PHP) php -v | grep -i xdebug || echo "$(YELLOW)Xdebug is disabled$(NC)" + +# ============================================================================ +# INFORMATION +# ============================================================================ + +php-version: ## Show PHP version + @$(EXEC_PHP) php -v + +php-info: ## Show PHP information + @$(EXEC_PHP) php -i + +php-extensions: ## List installed PHP extensions + @$(EXEC_PHP) php -m + +composer-version: ## Show Composer version + @$(EXEC_PHP) composer --version + +env: ## Show environment variables + @$(EXEC_PHP) printenv + +# ============================================================================ +# DOCUMENTATION +# ============================================================================ + +docs: ## Generate API documentation (requires phpDocumentor) + @echo "$(BLUE)📚 Generating documentation...$(NC)" + @$(EXEC_PHP) vendor/bin/phpdoc + @echo "$(GREEN)✓ Documentation generated in ./docs$(NC)" + +# ============================================================================ +# CI/CD SIMULATION +# ============================================================================ + +ci: install test check security ## Simulate CI pipeline + @echo "$(GREEN)✓ CI pipeline completed successfully$(NC)" + +# ============================================================================ +# ADVANCED OPERATIONS +# ============================================================================ + +profile: ## Profile application performance + @$(EXEC_PHP) vendor/bin/phpbench run --report=default + +benchmark: ## Run benchmarks + @$(EXEC_PHP) vendor/bin/phpbench run + +metrics: ## Generate code metrics + @$(EXEC_PHP) vendor/bin/phpmetrics --report-html=metrics src + +# ============================================================================ +# DEVELOPMENT HELPERS +# ============================================================================ + +watch-tests: ## Watch and run tests on file changes (requires watchman or inotifywait) + @echo "$(BLUE)👀 Watching for changes...$(NC)" + @while true; do \ + inotifywait -r -e modify src/ tests/ 2>/dev/null && make test; \ + done + +init: ## Initialize new component development environment + @echo "$(BLUE)🎬 Initializing development environment...$(NC)" + @$(MAKE) up + @$(MAKE) install + @echo "$(GREEN)✓ Development environment ready!$(NC)" + @echo "" + @echo "$(YELLOW)Next steps:$(NC)" + @echo " 1. Edit your code in ./src" + @echo " 2. Run $(GREEN)make test$(NC) to execute tests" + @echo " 3. Run $(GREEN)make qa$(NC) for complete quality assurance" + @echo "" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e73b57 --- /dev/null +++ b/README.md @@ -0,0 +1,640 @@ +# 🚀 KaririCode DevKit + +[![PHP Version](https://img.shields.io/badge/PHP-8.4%2B-blue)](https://www.php.net) +[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) +[![Code Quality](https://img.shields.io/badge/Code%20Quality-Level%20Max-brightgreen)](phpstan.neon) +[![Docker](https://img.shields.io/badge/Docker-Supported-blue)](docker-compose.yml) +[![Automated Setup](https://img.shields.io/badge/Setup-Automated-success)](install.sh) + +> **Professional development environment for KaririCode Framework components** +> Fully automated installation, standardized tooling, and production-ready setup for PHP 8.4+ projects + +--- + +## 📋 Table of Contents + +- [Overview](#-overview) +- [Features](#-features) +- [Requirements](#-requirements) +- [Quick Start](#-quick-start) +- [Usage](#-usage) +- [Available Commands](#-available-commands) +- [Configuration](#-configuration) +- [Best Practices](#-best-practices) +- [Troubleshooting](#-troubleshooting) +- [Contributing](#-contributing) +- [License](#-license) + +--- + +## 🎯 Overview + +**KaririCode DevKit** é um ambiente de desenvolvimento profissional e padronizado para todos os componentes do **KaririCode Framework**. Ele fornece: + +- **Ambiente containerizado** usando Docker com imagem centralizada +- **Ferramentas de qualidade de código** pré-configuradas (PHPStan, PHP CS Fixer, PHPMD) +- **Makefile profissional** com mais de 50 comandos úteis +- **Configurações consistentes** para garantir qualidade e manutenibilidade +- **Integração CI/CD** pronta para GitHub Actions + +### Princípios de Design + +- ✅ **SOLID** - Segregação de responsabilidades e inversão de dependência +- ✅ **Clean Code** - Código limpo, legível e manutenível +- ✅ **PSR Compliant** - Segue PSR-4, PSR-12 e PSR-6/16 +- ✅ **Strong Typing** - Tipagem forte em PHP 8.1+ +- ✅ **Test-Driven** - Estrutura completa para TDD +- ✅ **Performance-First** - Otimizado para produção + +--- + +## ✨ Features + +### 🐳 Docker Environment + +- Usa imagem centralizada: `kariricode/php-api-stack` +- Redis 7 Alpine para caching +- Memcached 1.6 Alpine para caching distribuído +- Xdebug 3 para debugging (ativação sob demanda) +- Configuração otimizada para desenvolvimento + +### 🛠️ Code Quality Tools + +| Tool | Version | Purpose | Config File | +|------|---------|---------|-------------| +| **PHPUnit** | 11.4 | Unit & Integration Testing | `phpunit.xml` | +| **PHPStan** | 2.0 | Static Analysis (Level Max) | `phpstan.neon` | +| **PHP CS Fixer** | 3.64 | Code Style (PSR-12+) | `.php-cs-fixer.php` | +| **PHPMD** | 2.15 | Mess Detection | `devkit/.config/phpmd/ruleset.xml` | +| **Rector** | 1.2 | Automated Refactoring | `rector.php` | +| **PHPBench** | 1.3 | Performance Benchmarking | N/A | + +### 📊 Coverage & Reports + +- **HTML Coverage Report** - Visual coverage analysis +- **Clover XML** - For CI integration +- **JUnit XML** - For CI integration +- **TestDox** - Human-readable test documentation + +### 🎨 Editor Integration + +- **EditorConfig** - Consistent formatting across editors +- **PHPStorm/VSCode** - Pre-configured settings +- **Git Hooks** - Pre-commit quality checks (optional) + +--- + +## 📦 Requirements + +### Essential + +- **Docker** >= 20.10 +- **Docker Compose** >= 2.0 +- **Make** (usually pre-installed on Unix systems) + +### Optional (for local development without Docker) + +- **PHP** >= 8.3 +- **Composer** >= 2.5 +- **Redis** (for cache testing) +- **Memcached** (for cache testing) + +--- + +## 🚀 Quick Start + +### Step 1: Clone for New Component + +```bash +# Clone the DevKit +git clone https://github.com/KaririCode-Framework/kariricode-devkit.git my-component + +cd my-component + +# Remove DevKit Git history +rm -rf .git + +# Initialize new repository +git init +``` + +### Step 2: Customize the Project + +```bash +# Edit composer.json with your component information +nano composer.json + +# Create directory structure +mkdir -p src tests/{Unit,Integration} +``` + +### Step 3: Initialize the Environment + +```bash +# Starts containers, installs dependencies +make init +``` + +### Step 4: Start Developing + +```bash +# Access container shell +make shell + +# Run tests +make test + +# Check code quality +make check +``` + +--- + +## 💻 Usage + +### Typical Development Flow + +```bash +# 1. Start environment +make up + +# 2. Install dependencies +make install + +# 3. Develop and test continuously +make test # Run all tests +make test-filter FILTER=MyTest # Test specific + +# 4. Check quality before commit +make qa # Fix code style + Run tests + Static analysis + +# 5. Stop environment +make down +``` + +### Git Integration + +```bash +# Recommended: run before each commit +make qa + +# Or configure pre-commit hook (optional) +cat << 'EOF' > .git/hooks/pre-commit +#!/bin/bash +make qa || exit 1 +EOF +chmod +x .git/hooks/pre-commit +``` + +--- + +## 📖 Available Commands + +### Docker Management + +```bash +make up # Start all containers +make down # Stop all containers +make restart # Restart containers +make status # Show container status +make logs # Show all logs +make logs-php # Show PHP container logs +make shell # Access PHP container shell +make shell-root # Access container as root +``` + +### Dependency Management + +```bash +make install # Install dependencies +make update # Update dependencies +make require PKG=vendor/package # Add package +make require-dev PKG=vendor/package # Add dev package +make autoload # Dump autoload +make validate # Validate composer.json +make outdated # Show outdated packages +``` + +### Testing + +```bash +make test # Run all tests +make test-coverage # Generate HTML coverage +make test-coverage-text # Show coverage in terminal +make test-unit # Run unit tests only +make test-integration # Run integration tests +make test-filter FILTER=TestName # Run specific test +``` + +### Code Quality + +```bash +make cs-check # Check code style (dry-run) +make cs-fix # Fix code style +make analyse # Run PHPStan (level max) +make analyse-baseline # Generate PHPStan baseline +make phpmd # Run PHP Mess Detector +make rector # Run Rector (dry-run) +make rector-fix # Apply Rector changes +``` + +### Comprehensive Checks + +```bash +make check # Run CS check + PHPStan + PHPMD +make fix # Fix all auto-fixable issues +make qa # Complete pipeline: fix + test + check +``` + +### Security + +```bash +make security # Check for vulnerabilities (composer audit) +``` + +### Cache Operations + +```bash +make redis-cli # Access Redis CLI +make redis-flush # Flush Redis cache +make redis-info # Show Redis information +make memcached-stats # Show Memcached statistics +make memcached-flush # Flush Memcached +``` + +### Utilities + +```bash +make clean # Clean generated files +make reset # Clean + remove containers +make rebuild # Complete rebuild from scratch +make permissions # Fix file permissions +``` + +### Xdebug + +```bash +make xdebug-on # Enable Xdebug +make xdebug-off # Disable Xdebug +make xdebug-status # Show Xdebug status +``` + +### Information + +```bash +make php-version # Show PHP version +make php-info # Show PHP configuration +make php-extensions # List PHP extensions +make composer-version # Show Composer version +make env # Show environment variables +``` + +### CI/CD Simulation + +```bash +make ci # Simulate complete CI pipeline +``` + +### Development Helpers + +```bash +make watch-tests # Watch and run tests on changes +make init # Initialize new component (up + install) +``` + +--- + +## ⚙️ Configuration + +### Environment Variables + +Create a `.env` file in the project root (optional): + +```env +# Project name (affects container names) +COMPOSE_PROJECT_NAME=kariricode_cache + +# Xdebug +XDEBUG_MODE=off + +# Redis +REDIS_PORT=6379 + +# Memcached +MEMCACHED_PORT=11211 +``` + +### Tool Customization + +#### PHPUnit + +Edit `phpunit.xml` to adjust: +- Test suites +- Coverage settings +- Environment variables + +#### PHPStan + +Edit `phpstan.neon` to: +- Adjust analysis level (not recommended to lower from `max`) +- Add excluded paths +- Ignore specific errors (use sparingly) + +#### PHP CS Fixer + +Edit `.php-cs-fixer.php` to: +- Customize style rules +- Add exceptions + +#### PHPMD + +Edit `devkit/.config/phpmd/ruleset.xml` to: +- Adjust complexity limits +- Disable specific rules + +### Recommended Directory Structure + +``` +your-component/ +├── devkit/ # DevKit files (do not commit to component) +│ └── .config/ +│ ├── php/ +│ │ ├── xdebug.ini +│ │ └── error-reporting.ini +│ └── phpmd/ +│ └── ruleset.xml +├── src/ # Component source code +│ ├── Adapter/ +│ ├── Contract/ +│ ├── Exception/ +│ └── ... +├── tests/ # Tests +│ ├── Unit/ +│ ├── Integration/ +│ └── bootstrap.php +├── .editorconfig +├── .gitignore +├── .php-cs-fixer.php +├── composer.json +├── docker-compose.yml +├── Makefile +├── phpstan.neon +├── phpunit.xml +├── README.md +└── LICENSE +``` + +--- + +## 🎯 Best Practices + +### 1. Always Use Strong Typing + +```php +name; + } +} +``` + +### 2. Follow SOLID + +```php +// ✅ Good: Dependency Injection +public function __construct( + private readonly CacheInterface $cache +) { +} + +// ❌ Bad: Direct instantiation +public function __construct() +{ + $this->cache = new RedisCache(); +} +``` + +### 3. Write Tests First (TDD) + +```php +// tests/Unit/ExampleTest.php +final class ExampleTest extends TestCase +{ + public function testItWorks(): void + { + $example = new Example('John', 30); + + $this->assertSame('John', $example->getName()); + } +} +``` + +### 4. Documente Código Complexo + +```php +/** + * Processa dados usando estratégia de cache multinível. + * + * Este método implementa cache em camadas com fallback automático. + * Primeiro tenta L1 (memória), depois L2 (Redis), e finalmente + * busca da fonte original. + * + * Performance characteristics: + * - Time complexity: O(1) para cache hit + * - Space complexity: O(n) onde n é o tamanho dos dados + * + * @param array $data Dados a processar + * @param positive-int $ttl Tempo de vida em segundos + * + * @return array Dados processados e cacheados + * + * @throws CacheException Se cache falhar + * @throws InvalidArgumentException Se dados inválidos + * + * @example + * ```php + * $result = $cache->process(['key' => 'value'], 3600); + * ``` + * + * @see https://kariricode.org/docs/cache/multilayer + * @since 1.0.0 + */ +public function process(array $data, int $ttl): array +{ + // Implementation +} +``` + +**Padrão de Documentação:** +- ✅ Descrição clara e concisa +- ✅ Explicação detalhada quando necessário +- ✅ Performance characteristics (opcional) +- ✅ Todos os @param com tipos precisos +- ✅ @return com tipo de retorno +- ✅ @throws para todas as exceções +- ✅ @example com código funcional +- ✅ @see para referências externas +- ✅ @since para versionamento + +> **PHP CS Fixer está configurado para MANTER toda a documentação!** +> Diferente de configurações padrão, nossa setup preserva @author, @package, +> @category, e todos os outros tags importantes. + +### 5. Use Make Commands + +```bash +# Antes de cada commit +make qa + +# Durante desenvolvimento +make test-filter FILTER=MyNewFeature +``` + +--- + +## 🔧 Troubleshooting + +### Common Issues + +#### 1. **Containers won't start** + +```bash +# Check if ports are in use +docker ps -a + +# Remove old containers +make clean +docker system prune -a + +# Rebuild +make rebuild +``` + +#### 2. **File permissions** + +```bash +# Fix permissions +make permissions + +# Or manually +docker-compose exec -u root php chown -R app:app /var/www +``` + +#### 3. **Dependencies won't install** + +```bash +# Clear Composer cache +docker-compose exec php composer clear-cache + +# Reinstall +make clean +make install +``` + +#### 4. **Xdebug not working** + +```bash +# Check if enabled +make xdebug-status + +# Enable +make xdebug-on + +# Configure IDE for port 9003 +# host: localhost +# port: 9003 +``` + +#### 5. **Tests fail on CI but pass locally** + +```bash +# Simulate CI environment +make ci + +# Check environment differences +docker-compose exec php php -v +docker-compose exec php php -m +``` + +### Logs and Debug + +```bash +# View all logs +make logs + +# Specific logs +make logs-php + +# Access shell for debugging +make shell +``` + +--- + +## 🤝 Contributing + +Contributions are welcome! To contribute: + +1. **Fork** the repository +2. **Create** a branch for your feature (`git checkout -b feature/amazing-feature`) +3. **Commit** your changes (`git commit -m 'Add amazing feature'`) +4. **Push** to the branch (`git push origin feature/amazing-feature`) +5. **Open** a Pull Request + +### Guidelines + +- Follow SOLID principles and Clean Code +- Maintain test coverage >= 80% +- Run `make qa` before committing +- Document changes in README if necessary +- Use [Conventional Commits](https://www.conventionalcommits.org/) + +--- + +## 📄 License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +--- + +## 🔗 Useful Links + +- **KaririCode Framework**: https://kariricode.org +- **Documentation**: https://kariricode.org/docs +- **GitHub Organization**: https://github.com/KaririCode-Framework +- **Packagist**: https://packagist.org/packages/kariricode/ + +--- + +## 👥 Maintainers + +- **Walmir Silva** - [walmir.silva@kariricode.org](mailto:walmir.silva@kariricode.org) + +--- + +## 🙏 Acknowledgments + +- PHP-FIG for PSR standards +- Symfony Cache Component for inspiration +- Docker community for containerization best practices +- All contributors to the KaririCode Framework + +--- + +**Built with ❤️ by the KaririCode Team** + +*Empowering developers to build robust, maintainable, and professional PHP applications* \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8d5c5bf --- /dev/null +++ b/composer.json @@ -0,0 +1,65 @@ +{ + "name": "kariricode/devkit", + "description": "Professional development environment for KaririCode Framework components", + "type": "project", + "keywords": [ + "kariricode", + "devkit", + "development-environment", + "docker", + "php" + ], + "license": "MIT", + "authors": [ + { + "name": "Walmir Silva", + "email": "walmir.silva@kariricode.org", + "homepage": "https://kariricode.org", + "role": "Developer" + } + ], + "homepage": "https://github.com/KaririCode-Framework/kariricode-devkit", + "support": { + "issues": "https://github.com/KaririCode-Framework/kariricode-devkit/issues", + "source": "https://github.com/KaririCode-Framework/kariricode-devkit", + "docs": "https://kariricode.org/docs/devkit" + }, + "require": { + "php": "^8.3" + }, + "require-dev": { + "phpunit/phpunit": "12.4.1", + "friendsofphp/php-cs-fixer": "^3.64", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpmd/phpmd": "^2.15", + "rector/rector": "2.2.3" + }, + "scripts": { + "post-install-cmd": [ + "@php -r \"if (!file_exists('.env')) { copy('.env.example', '.env'); echo 'Created .env file from .env.example\\n'; }\"" + ], + "test": "echo 'DevKit base - no tests. Use install.sh to create a component.'", + "check-ready": [ + "@php -r \"echo '\\n🔍 Checking DevKit readiness...\\n\\n';\"", + "@php -r \"echo (version_compare(PHP_VERSION, '8.3.0') >= 0 ? '✓' : '✗') . ' PHP version: ' . PHP_VERSION . '\\n';\"", + "@php -r \"echo (extension_loaded('redis') ? '✓' : '⚠') . ' Redis extension\\n';\"", + "@php -r \"echo (extension_loaded('memcached') ? '✓' : '⚠') . ' Memcached extension\\n';\"", + "@php -r \"echo (extension_loaded('xdebug') ? '✓' : '⚠') . ' Xdebug extension\\n';\"", + "@php -r \"echo '\\n📦 DevKit is ready! Run ./install.sh to create a component.\\n\\n';\"" + ] + }, + "scripts-descriptions": { + "check-ready": "Verify DevKit environment is properly configured" + }, + "config": { + "sort-packages": true, + "optimize-autoloader": true, + "preferred-install": "dist", + "allow-plugins": { + "php-http/discovery": true + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} \ No newline at end of file diff --git a/devkit/.config/php/error-reporting.ini b/devkit/.config/php/error-reporting.ini new file mode 100644 index 0000000..8fec4ec --- /dev/null +++ b/devkit/.config/php/error-reporting.ini @@ -0,0 +1,145 @@ +; ============================================================================ +; KaririCode DevKit - Error Reporting Configuration +; ============================================================================ +; Strict error reporting configuration for development environment +; All errors, warnings, and notices are displayed to catch issues early +; +; Location: devkit/.config/php/error-reporting.ini +; ============================================================================ + +[PHP] +; ============================================================================ +; ERROR REPORTING +; ============================================================================ +; Report all errors, warnings, and notices +error_reporting = E_ALL + +; Display errors on screen (development only) +display_errors = On +display_startup_errors = On + +; Log errors to file +log_errors = On +error_log = /var/log/php_errors.log + +; Detailed error messages +html_errors = On +docref_root = "https://www.php.net/manual/en/" +docref_ext = .html + +; Track all errors +track_errors = Off +xmlrpc_errors = Off + +; ============================================================================ +; ASSERTIONS +; ============================================================================ +; Enable assertions for development +zend.assertions = 1 +assert.active = 1 +assert.exception = 1 +assert.bail = 0 + +; ============================================================================ +; DEVELOPMENT SETTINGS +; ============================================================================ +; Hide PHP version in headers (security) +expose_php = Off + +; Variables order +variables_order = "EGPCS" +request_order = "GP" + +; Auto-detect line endings +auto_detect_line_endings = Off + +; ============================================================================ +; RESOURCE LIMITS +; ============================================================================ +; Memory limit (generous for development) +memory_limit = 512M + +; Maximum execution time +max_execution_time = 30 +max_input_time = 60 + +; Input size limits +post_max_size = 25M +upload_max_filesize = 20M +max_file_uploads = 20 + +; ============================================================================ +; OUTPUT BUFFERING +; ============================================================================ +; Output buffering (off for immediate error display) +output_buffering = Off +implicit_flush = On + +; ============================================================================ +; DATE/TIME +; ============================================================================ +; Default timezone +date.timezone = UTC + +; ============================================================================ +; SESSION +; ============================================================================ +; Session configuration +session.save_handler = files +session.save_path = "/tmp" +session.use_strict_mode = 1 +session.use_cookies = 1 +session.use_only_cookies = 1 +session.cookie_httponly = 1 +session.cookie_secure = 0 +session.cookie_samesite = "Lax" +session.gc_probability = 1 +session.gc_divisor = 100 +session.gc_maxlifetime = 1440 + +; ============================================================================ +; REALPATH CACHE +; ============================================================================ +; Realpath cache (keep small for development) +realpath_cache_size = 4096K +realpath_cache_ttl = 120 + +; ============================================================================ +; FILE UPLOADS +; ============================================================================ +; File uploads enabled +file_uploads = On +upload_tmp_dir = /tmp + +; ============================================================================ +; SECURITY +; ============================================================================ +; Disable dangerous functions (customize as needed) +disable_functions = +disable_classes = + +; ============================================================================ +; MAIL +; ============================================================================ +; Mail configuration (usually handled by application) +SMTP = localhost +smtp_port = 25 +sendmail_path = /usr/sbin/sendmail -t -i + +; ============================================================================ +; MISC +; ============================================================================ +; Allow URL fopen (needed for many libraries) +allow_url_fopen = On +allow_url_include = Off + +; Auto-prepend/append files +auto_prepend_file = +auto_append_file = + +; Default charset +default_charset = "UTF-8" + +; Maximum input variables (prevent resource exhaustion) +max_input_vars = 1000 +max_input_nesting_level = 64 \ No newline at end of file diff --git a/devkit/.config/php/opcache.ini b/devkit/.config/php/opcache.ini new file mode 100644 index 0000000..32af5cc --- /dev/null +++ b/devkit/.config/php/opcache.ini @@ -0,0 +1,15 @@ +; --- OPcache base (FPM + CLI) --- +zend_extension=opcache.so + +opcache.enable=1 +opcache.enable_cli=1 + +; Tuning sugerido p/ dev +opcache.memory_consumption=192 +opcache.interned_strings_buffer=16 +opcache.max_accelerated_files=20000 +opcache.validate_timestamps=1 +opcache.revalidate_freq=2 +opcache.fast_shutdown=1 +; Evita cache de arquivos que mudam muito em dev, se preferir +; opcache.file_update_protection=0 diff --git a/devkit/.config/php/xdebug.ini b/devkit/.config/php/xdebug.ini new file mode 100644 index 0000000..462643e --- /dev/null +++ b/devkit/.config/php/xdebug.ini @@ -0,0 +1,160 @@ +; ============================================================================ +; KaririCode DevKit - Xdebug Configuration +; ============================================================================ +; Xdebug 3.x configuration for step debugging and code coverage +; https://xdebug.org/docs/all_settings +; +; Location: devkit/.config/php/xdebug.ini +; ============================================================================ + +[xdebug] +; ============================================================================ +; MODE CONFIGURATION +; ============================================================================ +; Modes: off, develop, coverage, debug, gcstats, profile, trace +; Multiple modes can be combined with commas (e.g., "debug,coverage") +; This is controlled by environment variable XDEBUG_MODE in .env +xdebug.mode=${XDEBUG_MODE} + +; ============================================================================ +; DEBUGGING +; ============================================================================ +; Start debugging automatically or wait for trigger +; Values: yes, no, trigger +xdebug.start_with_request=yes + +; IDE/Client connection settings +; Use host.docker.internal for Docker Desktop (Mac/Windows) +; Use 172.17.0.1 for Docker on Linux +xdebug.client_host=host.docker.internal +xdebug.client_port=9003 + +; Discovery mode for cloud/dynamic environments +; Set to 1 if you need automatic discovery (not recommended for local dev) +xdebug.discover_client_host=0 + +; IDE key for identifying debugging session +; PHPStorm: PHPSTORM +; VSCode: VSCODE +xdebug.idekey=PHPSTORM + +; Connection timeout in milliseconds +xdebug.connect_timeout_ms=2000 + +; ============================================================================ +; LOGGING +; ============================================================================ +; Log file location (useful for debugging connection issues) +xdebug.log=/var/log/xdebug.log + +; Log level (0-10, where 10 is most verbose) +; 0 = Criticals +; 1 = Errors +; 3 = Warnings +; 5 = Communication +; 7 = Information +; 10 = Debug +xdebug.log_level=7 + +; ============================================================================ +; STEP DEBUGGING +; ============================================================================ +; Maximum nesting level for recursive debugging +; Increase if you have deeply nested structures +xdebug.max_nesting_level=512 + +; ============================================================================ +; COVERAGE +; ============================================================================ +; Enable code coverage (required for PHPUnit coverage) +xdebug.coverage_enable=1 + +; ============================================================================ +; DEVELOPMENT MODE +; ============================================================================ +; Development helpers (when mode=develop) +; Show local variables in stack traces +xdebug.dump.GET=* +xdebug.dump.POST=* +xdebug.dump.COOKIE=* +xdebug.dump.FILES=* +xdebug.dump.SESSION=* + +; ============================================================================ +; PROFILING (disabled by default) +; ============================================================================ +; Uncomment to enable profiling +; xdebug.profiler_enable=0 +; xdebug.profiler_enable_trigger=1 +; xdebug.profiler_enable_trigger_value="" +; xdebug.profiler_output_dir=/var/www/profiler +; xdebug.profiler_output_name=cachegrind.out.%p + +; ============================================================================ +; TRACING (disabled by default) +; ============================================================================ +; Uncomment to enable function tracing +; xdebug.trace_enable_trigger=1 +; xdebug.trace_enable_trigger_value="" +; xdebug.trace_output_dir=/var/www/traces +; xdebug.trace_output_name=trace.%c +; xdebug.trace_format=0 +; xdebug.trace_options=0 + +; ============================================================================ +; PERFORMANCE +; ============================================================================ +; Show memory usage in stack traces +xdebug.show_mem_delta=1 + +; ============================================================================ +; DISPLAY +; ============================================================================ +; HTML error output formatting +xdebug.cli_color=1 + +; Variable display depth +xdebug.var_display_max_depth=10 + +; Maximum number of array children/object properties +xdebug.var_display_max_children=256 + +; Maximum string length +xdebug.var_display_max_data=4096 + +; ============================================================================ +; USAGE TIPS +; ============================================================================ +; +; Enable Xdebug: +; make xdebug-on +; +; Disable Xdebug: +; make xdebug-off +; +; Check Status: +; make xdebug-status +; +; IDE Configuration: +; PHPStorm: +; - Settings > PHP > Debug +; - Port: 9003 +; - Check "Accept external connections" +; - Settings > PHP > Servers +; - Add server: localhost, port 9003 +; - Map: /var/www -> your-project-path +; +; VSCode: +; - Install PHP Debug extension +; - Add to launch.json: +; { +; "name": "Listen for Xdebug", +; "type": "php", +; "request": "launch", +; "port": 9003, +; "pathMappings": { +; "/var/www": "${workspaceFolder}" +; } +; } +; +; ============================================================================ \ No newline at end of file diff --git a/devkit/.config/phpmd/ruleset.xml b/devkit/.config/phpmd/ruleset.xml new file mode 100644 index 0000000..0c05d4d --- /dev/null +++ b/devkit/.config/phpmd/ruleset.xml @@ -0,0 +1,273 @@ + + + + + + Professional PHPMD ruleset for KaririCode Framework components. + Enforces clean code principles, SOLID design, and maintainability. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */tests/* + */Tests/* + + + */vendor/* + + + */cache/* + */storage/* + */temp/* + */tmp/* + + + */build/* + */coverage/* + */docs/* + + + */migrations/* + */database/factories/* + */database/seeders/* + + + */_ide_helper*.php + *.blade.php + + + */config/* + + + */public/* + + + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f104595 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,54 @@ +services: + php: + image: kariricode/php-api-stack:dev + container_name: ${APP_NAME:-kariricode-devkit}_php + working_dir: /var/www/html + env_file: + - .env + ports: + # Expose the application port to the host + - "${APP_PORT:-8089}:80" + # Expose the internal redis port to the host if REDIS_HOST_PORT is set + - "${REDIS_PORT:-6379}:6379" + volumes: + - ./:/var/www/html + - ./devkit/.config/php/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini:ro + - ./devkit/.config/php/error-reporting.ini:/usr/local/etc/php/conf.d/error-reporting.ini:ro + - ./devkit/.config/php/opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini:ro + environment: + APP_ENV: ${APP_ENV:-development} + APP_DEBUG: ${APP_DEBUG:-true} + DEMO_MODE: ${DEMO_MODE:-false} + HEALTH_CHECK_INSTALL: ${HEALTH_CHECK_INSTALL:-true} + COMPOSER_MEMORY_LIMIT: ${COMPOSER_MEMORY_LIMIT:--1} + XDEBUG_MODE: ${XDEBUG_MODE:-off} + XDEBUG_CONFIG: client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal} + networks: + - kariricode_network + extra_hosts: + - host.docker.internal:host-gateway + depends_on: + memcached: + condition: service_healthy + + memcached: + image: memcached:1.6-alpine + container_name: ${APP_NAME:-kariricode-devkit}_memcached + command: memcached -m ${MEMCACHED_MEMORY:-256} + env_file: + - .env + ports: + - "${MEMCACHED_PORT:-11211}:11211" + networks: + - kariricode_network + healthcheck: + test: ["CMD", "nc", "-z", "127.0.0.1", "11211"] + interval: 10s + timeout: 3s + retries: 3 + start_period: 5s + +networks: + kariricode_network: + driver: bridge + name: ${APP_NAME:-kariricode-devkit}_network diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..c9bd478 --- /dev/null +++ b/install.sh @@ -0,0 +1,638 @@ +#!/bin/bash + +################################################################################ +# KaririCode DevKit - Professional Component Installer +################################################################################ +# This script automates the setup of a new KaririCode Framework component +# with a fully configured development environment. +# +# Usage: ./install.sh +################################################################################ + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Emoji support +CHECK="✓" +CROSS="✗" +ARROW="→" +ROCKET="🚀" +PACKAGE="📦" +WRENCH="🔧" +TEST="🧪" +CLEAN="🧹" + +################################################################################ +# UTILITY FUNCTIONS +################################################################################ + +print_header() { + echo "" + echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" + echo -e "${BLUE}║ KaririCode DevKit - Component Installation Wizard ║${NC}" + echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}" + echo "" +} + +print_step() { + echo -e "${BLUE}${ARROW}${NC} $1" +} + +print_success() { + echo -e "${GREEN}${CHECK}${NC} $1" +} + +print_error() { + echo -e "${RED}${CROSS}${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}!${NC} $1" +} + +prompt_input() { + local prompt="$1" + local var_name="$2" + local default="$3" + + if [ -n "$default" ]; then + read -p "$(echo -e ${BLUE}${prompt}${NC} [${default}]: )" input + eval $var_name="${input:-$default}" + else + read -p "$(echo -e ${BLUE}${prompt}${NC}: )" input + eval $var_name="$input" + fi +} + +prompt_confirm() { + local prompt="$1" + read -p "$(echo -e ${YELLOW}${prompt}${NC} [y/N]: )" -r + echo + [[ $REPLY =~ ^[Yy]$ ]] +} + +validate_namespace() { + local namespace="$1" + if [[ ! "$namespace" =~ ^[A-Z][a-zA-Z0-9]*$ ]]; then + return 1 + fi + return 0 +} + +################################################################################ +# MAIN INSTALLATION STEPS +################################################################################ + +collect_information() { + print_step "Collecting component information..." + echo "" + + # Component Name + while true; do + prompt_input "Component name (e.g., Cache, Validator, Router)" COMPONENT_NAME + + if [ -z "$COMPONENT_NAME" ]; then + print_error "Component name cannot be empty" + continue + fi + + if ! validate_namespace "$COMPONENT_NAME"; then + print_error "Name must start with uppercase letter and contain only letters and numbers" + continue + fi + + break + done + + # Convert to lowercase for package name + PACKAGE_NAME=$(echo "$COMPONENT_NAME" | sed 's/\([A-Z]\)/-\1/g' | sed 's/^-//' | tr '[:upper:]' '[:lower:]') + + # Description + prompt_input "Component description" COMPONENT_DESCRIPTION "Professional ${COMPONENT_NAME} implementation for KaririCode Framework" + + # Author information + prompt_input "Author name" AUTHOR_NAME "Walmir Silva" + prompt_input "Author email" AUTHOR_EMAIL "walmir.silva@kariricode.org" + + # PHP Version + prompt_input "Minimum PHP version" PHP_VERSION "8.4" + + echo "" + print_success "Information collected successfully!" + echo "" + echo -e "${BLUE}Component:${NC} $COMPONENT_NAME" + echo -e "${BLUE}Package:${NC} kariricode/$PACKAGE_NAME" + echo -e "${BLUE}Namespace:${NC} KaririCode\\$COMPONENT_NAME" + echo -e "${BLUE}Description:${NC} $COMPONENT_DESCRIPTION" + echo "" + + if ! prompt_confirm "Confirm the information above?"; then + print_error "Installation cancelled by user" + exit 0 + fi +} + +create_directory_structure() { + print_step "Creating directory structure..." + + mkdir -p src/{Adapter,Contract,Exception,Factory} + print_success "Created: src/ with subdirectories" + + mkdir -p tests/{Unit,Integration,Fixtures} + print_success "Created: tests/ with subdirectories" + + mkdir -p docs + print_success "Created: docs/" + + # Create .gitkeep files + touch src/.gitkeep + touch tests/Unit/.gitkeep + touch tests/Integration/.gitkeep + touch tests/Fixtures/.gitkeep + + print_success "Directory structure created" +} + +generate_composer_json() { + print_step "Generating composer.json..." + + cat > composer.json < README.md < .env < /dev/null; then + print_error "Docker is not installed" + return 1 + fi + + if ! command -v docker-compose &> /dev/null; then + print_error "Docker Compose is not installed" + return 1 + fi + + docker-compose up -d + + # Wait for containers to be ready + print_step "Waiting for containers to be ready..." + sleep 5 + + print_success "Docker environment started" +} + +install_dependencies() { + print_step "Installing Composer dependencies..." + + docker-compose exec -T php composer install --no-interaction --prefer-dist + + print_success "Dependencies installed" +} + +run_tests() { + print_step "Running environment tests..." + + # Test PHP version + print_step "Checking PHP version..." + docker-compose exec -T php php -v + + # Test Composer + print_step "Checking Composer..." + docker-compose exec -T php composer --version + + # Test autoload + print_step "Testing autoload..." + docker-compose exec -T php composer dump-autoload + + print_success "Environment tests completed" +} + +run_quality_checks() { + print_step "Running quality checks..." + + # Validate composer.json + print_step "Validating composer.json..." + docker-compose exec -T php composer validate --strict + + # Check PHP syntax + print_step "Checking PHP syntax..." + docker-compose exec -T php find src -name "*.php" -print0 | xargs -0 -n1 php -l + + print_success "Quality checks completed" +} + +run_installation_checklist() { + print_step "Running installation checklist..." + echo "" + + local all_passed=true + + # Check directories + echo -e "${BLUE}Checking directory structure:${NC}" + for dir in src tests docs devkit; do + if [ -d "$dir" ]; then + print_success "Directory $dir exists" + else + print_error "Directory $dir not found" + all_passed=false + fi + done + echo "" + + # Check configuration files + echo -e "${BLUE}Checking configuration files:${NC}" + for file in composer.json docker-compose.yml Makefile phpunit.xml phpstan.neon .php-cs-fixer.php; do + if [ -f "$file" ]; then + print_success "File $file exists" + else + print_error "File $file not found" + all_passed=false + fi + done + echo "" + + # Check Docker containers + echo -e "${BLUE}Checking Docker containers:${NC}" + if docker-compose ps | grep -q "Up"; then + print_success "Containers are running" + else + print_error "Containers are not running" + all_passed=false + fi + echo "" + + # Check vendor directory + echo -e "${BLUE}Checking dependencies:${NC}" + if [ -d "vendor" ]; then + print_success "Dependencies installed" + else + print_error "Dependencies not installed" + all_passed=false + fi + echo "" + + if $all_passed; then + print_success "All checks passed!" + return 0 + else + print_error "Some checks failed" + return 1 + fi +} + +cleanup_installation_files() { + print_step "Removing installation files..." + + # Remove installation files + rm -f install.sh + print_success "Removed: install.sh" + + # Remove DevKit .git if exists + if [ -d ".git" ]; then + rm -rf .git + print_success "Removed: .git (DevKit history)" + fi + + # Remove template files + rm -f .github/workflows/devkit-*.yml 2>/dev/null || true + + print_success "Installation files removed" +} + +initialize_git_repository() { + print_step "Initializing Git repository..." + + git init + git add . + git commit -m "feat: initial commit - KaririCode ${COMPONENT_NAME} component + +- Setup development environment +- Configure quality tools +- Add basic project structure + +Generated by KaririCode DevKit" + + print_success "Git repository initialized" +} + +display_next_steps() { + echo "" + echo -e "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}" + echo -e "${GREEN}║ Installation Completed Successfully! ${ROCKET} ║${NC}" + echo -e "${GREEN}╚════════════════════════════════════════════════════════════════╝${NC}" + echo "" + echo -e "${BLUE}${PACKAGE} Component:${NC} KaririCode ${COMPONENT_NAME}" + echo -e "${BLUE}${PACKAGE} Namespace:${NC} KaririCode\\${COMPONENT_NAME}" + echo -e "${BLUE}${PACKAGE} Package:${NC} kariricode/${PACKAGE_NAME}" + echo "" + echo -e "${YELLOW}Next steps:${NC}" + echo "" + echo -e " ${ARROW} Access container shell:" + echo -e " ${GREEN}make shell${NC}" + echo "" + echo -e " ${ARROW} Create your classes in ${BLUE}src/${NC}" + echo -e " ${GREEN}src/Contract/${COMPONENT_NAME}Interface.php${NC}" + echo -e " ${GREEN}src/${COMPONENT_NAME}.php${NC}" + echo "" + echo -e " ${ARROW} Write tests in ${BLUE}tests/${NC}" + echo -e " ${GREEN}tests/Unit/${COMPONENT_NAME}Test.php${NC}" + echo "" + echo -e " ${ARROW} Run tests:" + echo -e " ${GREEN}make test${NC}" + echo "" + echo -e " ${ARROW} Check code quality:" + echo -e " ${GREEN}make qa${NC}" + echo "" + echo -e " ${ARROW} See all available commands:" + echo -e " ${GREEN}make help${NC}" + echo "" + echo -e "${BLUE}Documentation:${NC} https://kariricode.org/docs" + echo -e "${BLUE}GitHub:${NC} https://github.com/KaririCode-Framework" + echo "" + echo -e "${GREEN}Happy coding! ${ROCKET}${NC}" + echo "" +} + +################################################################################ +# MAIN EXECUTION +################################################################################ + +main() { + clear + print_header + + # Step 1: Collect information + collect_information + + # Step 2: Create directory structure + create_directory_structure + + # Step 3: Generate composer.json + generate_composer_json + + # Step 4: Generate README + generate_readme + + # Step 5: Update .env + update_env_file + + # Step 6: Start Docker environment + if ! start_docker_environment; then + print_error "Falha ao iniciar ambiente Docker" + exit 1 + fi + + # Step 7: Install dependencies + install_dependencies + + # Step 8: Run tests + run_tests + + # Step 9: Run quality checks + run_quality_checks + + # Step 10: Run checklist + if ! run_installation_checklist; then + print_warning "Some checks failed, but installation can continue" + fi + + # Step 11: Cleanup + cleanup_installation_files + + # Step 12: Initialize Git + initialize_git_repository + + # Step 13: Display next steps + display_next_steps +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..0582e97 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,129 @@ +# +# KaririCode DevKit - PHPStan Configuration +# +# Professional static analysis configuration with maximum strictness level. +# Ensures type safety, catches bugs early, and enforces best practices. +# +# @see https://phpstan.org/config-reference +# @see https://phpstan.org/user-guide/rule-levels +# + +parameters: + # Analysis Level (0-9, 9 being the strictest) + level: max + + # Paths to analyze + paths: + - src + - tests + + # Files/directories to exclude + excludePaths: + - tests/Fixtures/* + - vendor/* + + # Report unmatched ignored errors + reportUnmatchedIgnoredErrors: true + + # Treat PHPDoc types as authoritative + treatPhpDocTypesAsCertain: true + + # Check missing typehints + checkMissingCallableSignature: true + checkUninitializedProperties: true + checkTooWideReturnTypesInProtectedAndPublicMethods: true + checkImplicitMixed: true + checkBenevolentUnionTypes: true + + # Universal object crates (disable magic get/set for strict typing) + universalObjectCratesClasses: [] + + # Type aliases for better readability + typeAliases: + mixed: 'mixed' + + # Bleeding edge features (use latest PHPStan capabilities) + polluteScopeWithLoopInitialAssignments: false + polluteScopeWithAlwaysIterableForeach: false + checkAlwaysTrueCheckTypeFunctionCall: true + checkAlwaysTrueInstanceof: true + checkAlwaysTrueStrictComparison: true + checkExplicitMixedMissingReturn: true + checkFunctionNameCase: true + checkInternalClassCaseSensitivity: true + checkMissingIterableValueType: true + checkMissingVarTagTypehint: true + + # Exception handling + exceptions: + check: + missingCheckedExceptionInThrows: true + tooWideThrowType: true + checkedExceptionClasses: [] + implicitThrows: true + + # Ignore errors (use sparingly, prefer fixing the code) + ignoreErrors: + # Example: ignore specific errors if absolutely necessary + # - '#PHPDoc tag @var for variable \$.*#' + + # Bootstrap file (if needed for setup) + # bootstrapFiles: + # - tests/bootstrap.php + + # Parallel processing + parallel: + jobSize: 20 + maximumNumberOfProcesses: 4 + minimumNumberOfJobsPerProcess: 2 + + # Include PHPStan extensions + includes: + # Uncomment as needed: + # - vendor/phpstan/phpstan-phpunit/extension.neon + # - vendor/phpstan/phpstan-strict-rules/rules.neon + # - vendor/phpstan/phpstan-deprecation-rules/rules.neon + + # Stub files (for better understanding of external code) + stubFiles: [] + + # Custom rules + # customRulesetUsed: true + +# Services and rules configuration +services: + # Enable bleeding edge rules + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanAndRule + tags: + - phpstan.rules.rule + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule + tags: + - phpstan.rules.rule + - + class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule + tags: + - phpstan.rules.rule + - + class: PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule + tags: + - phpstan.rules.rule + - + class: PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule + tags: + - phpstan.rules.rule + - + class: PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule + tags: + - phpstan.rules.rule + +# Conditional configuration based on installed extensions +conditionalTags: + PHPStan\Rules\PHPUnit\*: + phpstan.rules.rule: %phpunit.enabled% + +# Include strict rules if available +# Uncomment when phpstan/phpstan-strict-rules is installed +# includes: +# - vendor/phpstan/phpstan-strict-rules/rules.neon \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e69de29 diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..29bb6aa --- /dev/null +++ b/rector.php @@ -0,0 +1,154 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->withSkip([ + // Skip test fixtures + __DIR__ . '/tests/Fixtures', + ]) + // PHP 8.3 features and best practices + ->withPhpSets(php83: true) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + codingStyle: true, + typeDeclarations: true, + privatization: true, + naming: true, + instanceOf: true, + earlyReturn: true, + strictBooleans: true + ) + ->withSets([ + LevelSetList::UP_TO_PHP_83, + SetList::TYPE_DECLARATION, + SetList::EARLY_RETURN, + SetList::CODE_QUALITY, + PHPUnitSetList::PHPUNIT_100, + PHPUnitSetList::PHPUNIT_CODE_QUALITY, + ]) + ->withRules([ + // PHP 8.0+ + InlineConstructorDefaultToPropertyRector::class, + ClassPropertyAssignToConstructorPromotionRector::class, + MixedTypeRector::class, + + // PHP 8.1+ + ReadOnlyPropertyRector::class, + FinalizePublicClassConstantRector::class, + FirstClassCallableRector::class, + + // PHP 8.2+ + ReadOnlyClassRector::class, + + // PHP 8.3+ + AddOverrideAttributeToOverriddenMethodsRector::class, + + // Code Quality + FlipTypeControlToUseExclusiveTypeRector::class, + RemoveUnusedPrivatePropertyRector::class, + RemoveUnusedPrivateMethodParameterRector::class, + RemoveUselessParamTagRector::class, + RemoveUselessReturnTagRector::class, + RemoveConcatAutocastRector::class, + RemoveAlwaysTrueIfConditionRector::class, + + // Coding Style + StaticClosureRector::class, + StaticArrowFunctionRector::class, + CatchExceptionNameMatchingTypeRector::class, + MakeInheritedMethodVisibilitySameAsParentRector::class, + UseClassKeywordForClassNameResolutionRector::class, + CountArrayToEmptyArrayComparisonRector::class, + PostIncDecToPreIncDecRector::class, + + // Early Return + ChangeAndIfToEarlyReturnRector::class, + ChangeIfElseValueAssignToEarlyReturnRector::class, + ChangeOrIfContinueToMultiContinueRector::class, + PreparedValueToEarlyReturnRector::class, + + // Type Declarations + AddVoidReturnTypeWhereNoReturnRector::class, + ReturnTypeFromStrictNativeCallRector::class, + TypedPropertyFromStrictConstructorRector::class, + EmptyOnNullableObjectToInstanceOfRector::class, + + // Naming + RenameVariableToMatchMethodCallReturnTypeRector::class, + RenameParamToMatchTypeRector::class, + + // Strict Rules + DisallowedEmptyRuleFixerRector::class, + + // PHPUnit + PreferPHPUnitThisCallRector::class, + ]) + ->withSkipRules([ + // Skip rules that conflict with our code style + EncapsedStringsToSprintfRector::class, + NewlineAfterStatementRector::class, + ]) + ->withImportNames( + importShortClasses: false, + removeUnusedImports: true + ) + ->withParallel() + ->withCache( + cacheDirectory: __DIR__ . '/.rector_cache' + ); From de7a747b1c1ef74e9a441cf4c900bad61a50dae0 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Wed, 22 Oct 2025 19:12:29 -0300 Subject: [PATCH 4/7] feat(domain): add UserProfile domain model with value objects, enums and tests - Introduced new domain entities: - `UserProfile` aggregate with immutability and rich behaviors - Value Objects: `Email`, `UserId` - Supporting attribute: `Length` - Enums: `UserRole`, `UserStatus` - Added full test coverage: - Unit tests for enums, value objects, and UserProfile - Integration test covering full user lifecycle flow - Updated configuration and tooling: - Adjusted `.gitignore` and `Makefile` - Updated `composer.json` dependencies and autoload - Refined `phpunit.xml` for PHPUnit 12 compatibility --- .gitignore | 14 - Makefile | 2 +- composer.json | 14 +- phpunit.xml | 56 ++++ src/Email.php | 44 +++ src/Length.php | 29 ++ src/UserId.php | 44 +++ src/UserProfile.php | 327 ++++++++++++++++++++++ src/UserRole.php | 62 ++++ src/UserStatus.php | 29 ++ tests/Integration/UserProfileFlowTest.php | 91 ++++++ tests/Unit/EnumsTest.php | 65 +++++ tests/Unit/UserProfileTest.php | 239 ++++++++++++++++ tests/Unit/ValueObjectsTest.php | 74 +++++ 14 files changed, 1073 insertions(+), 17 deletions(-) create mode 100644 src/Email.php create mode 100644 src/Length.php create mode 100644 src/UserId.php create mode 100644 src/UserProfile.php create mode 100644 src/UserRole.php create mode 100644 src/UserStatus.php create mode 100644 tests/Integration/UserProfileFlowTest.php create mode 100644 tests/Unit/EnumsTest.php create mode 100644 tests/Unit/UserProfileTest.php create mode 100644 tests/Unit/ValueObjectsTest.php diff --git a/.gitignore b/.gitignore index d65403b..bb22189 100644 --- a/.gitignore +++ b/.gitignore @@ -124,20 +124,6 @@ $RECYCLE.BIN/ # Created by php-api-stack image for demo/testing /public/ -# ============================================================================ -# COMPONENT DEVELOPMENT -# ============================================================================ -# When developing specific components, these are component-specific -# DevKit base doesn't have src/tests, but components created will -/src/ -/tests/Unit/ -/tests/Integration/ -/tests/Fixtures/ - -# Keep base test structure -!/tests/ -!/tests/.gitkeep - # ============================================================================ # DOCUMENTATION BUILD # ============================================================================ diff --git a/Makefile b/Makefile index ba8af12..fed8de1 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,7 @@ outdated: ## Show outdated packages test: ## Run all tests @echo "$(BLUE)🧪 Running tests...$(NC)" - @$(EXEC_PHP) vendor/bin/phpunit + @$(EXEC_PHP) vendor/bin/phpunit --no-coverage --testdox @echo "$(GREEN)✓ Tests completed$(NC)" test-coverage: ## Run tests with coverage report (HTML) diff --git a/composer.json b/composer.json index 8d5c5bf..acb63f4 100644 --- a/composer.json +++ b/composer.json @@ -24,16 +24,26 @@ "source": "https://github.com/KaririCode-Framework/kariricode-devkit", "docs": "https://kariricode.org/docs/devkit" }, + "autoload": { + "psr-4": { + "KaririCode\\DevKit\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "KaririCode\\DevKit\\Tests\\": "tests/" + } + }, "require": { "php": "^8.3" }, "require-dev": { "phpunit/phpunit": "12.4.1", - "friendsofphp/php-cs-fixer": "^3.64", + "friendsofphp/php-cs-fixer": "3.89.0", "phpstan/phpstan": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpmd/phpmd": "^2.15", - "rector/rector": "2.2.3" + "rector/rector": "2.2.4" }, "scripts": { "post-install-cmd": [ diff --git a/phpunit.xml b/phpunit.xml index e69de29..6afc55f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -0,0 +1,56 @@ + + + + + + tests/Unit + + + tests/Integration + + + + + + + src + + + vendor + devkit + tests + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Email.php b/src/Email.php new file mode 100644 index 0000000..73d16de --- /dev/null +++ b/src/Email.php @@ -0,0 +1,44 @@ + + * @copyright 2025 KaririCode + * @license MIT + * @version 1.0.0 + * @since 1.0.0 + */ +final readonly class Email implements Stringable, JsonSerializable +{ + public function __construct( + #[Length(min: 3, max: 320)] + public string $value, + ) { + if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { + throw new InvalidArgumentException("Invalid email: {$value}"); + } + } + + public function __toString(): string + { + return $this->value; + } + + public function jsonSerialize(): string + { + return $this->value; + } +} diff --git a/src/Length.php b/src/Length.php new file mode 100644 index 0000000..3c2baf4 --- /dev/null +++ b/src/Length.php @@ -0,0 +1,29 @@ + + * @copyright 2025 KaririCode + * @license MIT + * @version 1.0.0 + * @since 1.0.0 + */ +#[Attribute(Attribute::TARGET_PROPERTY)] +class Length +{ + public function __construct( + public readonly int $min = 0, + public readonly int $max = PHP_INT_MAX, + ) {} +} diff --git a/src/UserId.php b/src/UserId.php new file mode 100644 index 0000000..b3cac37 --- /dev/null +++ b/src/UserId.php @@ -0,0 +1,44 @@ + + * @copyright 2025 KaririCode + * @license MIT + * @version 1.0.0 + * @since 1.0.0 + */ +final readonly class UserId implements Stringable, JsonSerializable +{ + public function __construct( + #[Length(min: 1, max: 50)] + public string $value, + ) { + if (empty($value)) { + throw new InvalidArgumentException("User ID cannot be empty."); + } + } + + public function __toString(): string + { + return $this->value; + } + + public function jsonSerialize(): string + { + return $this->value; + } +} diff --git a/src/UserProfile.php b/src/UserProfile.php new file mode 100644 index 0000000..0255b1a --- /dev/null +++ b/src/UserProfile.php @@ -0,0 +1,327 @@ +with2FA('secret') + * ->promote(); + * + * echo $user->displayLabel(); // "Walmir Silva (editor)" + * ``` + * + * @package KaririCode\DevKit + * @category Query Filtering + * @author Walmir Silva + * @copyright 2025 KaririCode + * @license MIT + * @version 1.0.0 + * @since 1.0.0 + */ +final readonly class UserProfile implements JsonSerializable, Stringable +{ + public const string MODEL = 'UserProfile'; + public const int VERSION = 1; + + public function __construct( + #[Length(min: 1, max: 50)] + public string $id, + + #[Length(min: 2, max: 120)] + public string $name, + + public Email $email, + public UserRole $role = UserRole::VIEWER, + public UserStatus $status = UserStatus::ACTIVE, + + #[\SensitiveParameter] + public ?string $twoFactorSecret = null, + + public ?array $meta = null, + public ?DateTimeImmutable $createdAt = null, + public ?DateTimeImmutable $updatedAt = null, + ) {} + + /** + * Creates a new UserProfile instance with a generated ID and current timestamps. + * + * @param string $name The user's full name. + * @param Email|string $email The user's email address. Can be an Email object or a string. + * @param UserRole $role The user's role, defaults to VIEWER. + * @return UserProfile A new instance. + */ + public static function new( + string $name, + Email|string $email, + UserRole $role = UserRole::VIEWER, + ): self { + $now = new DateTimeImmutable(); + + return new self( + id: self::generateId(), + name: $name, + email: $email instanceof Email ? $email : new Email($email), + role: $role, + createdAt: $now, + updatedAt: $now, + ); + } + + /** + * Creates a UserProfile from a raw data array (e.g., from a database or API). + * This is the elegant, declarative hydration method. + * @param array $data An associative array containing user data. Expected keys: + * - 'id' (string, optional): User ID. If not provided, a new one will be generated. + * - 'name' (string): User's full name. + * - 'email' (string|Email): User's email address. + * - 'role' (string|UserRole, optional): User's role. Defaults to 'VIEWER'. + * - 'status' (string|UserStatus, optional): User's status. Defaults to 'ACTIVE'. + * - 'twoFactorSecret' (string, optional): Two-factor authentication secret. + * - 'meta' (array, optional): Additional metadata. + * - 'createdAt' (string|DateTimeImmutable, optional): Creation timestamp. + * - 'updatedAt' (string|DateTimeImmutable, optional): Last update timestamp. + * * @return UserProfile A new UserProfile instance hydrated with the provided data. + * @throws InvalidArgumentException If required data is missing or invalid. + */ + public static function fromArray(array $data): self + { + return new self( + id: $data['id'] ?? self::generateId(), + name: $data['name'] ?? '', + email: self::hydrateEmail($data), + role: self::hydrateRole($data), + status: self::hydrateStatus($data), + twoFactorSecret: $data['twoFactorSecret'] ?? null, + meta: $data['meta'] ?? null, + createdAt: self::hydrateDate($data, 'createdAt'), + updatedAt: self::hydrateDate($data, 'updatedAt'), + ); + } + + // ---------------------------------------------------------- + // Query Methods + // ---------------------------------------------------------- + + public function canEdit(): bool + { + return $this->status->isActive() && $this->role->canEdit(); + } + + public function has2FA(): bool + { + return $this->twoFactorSecret !== null; + } + + // ---------------------------------------------------------- + // Domain Behavior + // ---------------------------------------------------------- + + public function promote(): self + { + $newRole = $this->role === UserRole::ADMIN ? UserRole::ADMIN : UserRole::EDITOR; + + return new self( + id: $this->id, + name: $this->name, + email: $this->email, + role: $newRole, + status: $this->status, + twoFactorSecret: $this->twoFactorSecret, + meta: $this->meta, + createdAt: $this->createdAt, + updatedAt: new DateTimeImmutable() + ); + } + + public function suspend(): self + { + return new self( + id: $this->id, + name: $this->name, + email: $this->email, + role: $this->role, + status: UserStatus::SUSPENDED, + twoFactorSecret: $this->twoFactorSecret, + meta: $this->meta, + createdAt: $this->createdAt, + updatedAt: new DateTimeImmutable() + ); + } + + public function with2FA(#[\SensitiveParameter] string $secret): self + { + return new self( + id: $this->id, + name: $this->name, + email: $this->email, + role: $this->role, + status: $this->status, + twoFactorSecret: $secret, + meta: $this->meta, + createdAt: $this->createdAt, + updatedAt: new DateTimeImmutable() + ); + } + + public function withMeta(array $meta): self + { + return new self( + id: $this->id, + name: $this->name, + email: $this->email, + role: $this->role, + status: $this->status, + twoFactorSecret: $this->twoFactorSecret, + meta: $meta, + createdAt: $this->createdAt, + updatedAt: new DateTimeImmutable() + ); + } + + public function displayLabel(): string + { + return "{$this->name} ({$this->role->label()})"; + } + + // ---------------------------------------------------------- + // Serialization + // ---------------------------------------------------------- + + public function jsonSerialize(): array + { + return [ + 'model' => self::MODEL, + 'version' => self::VERSION, + 'id' => $this->id, + 'name' => $this->name, + 'email' => (string)$this->email, + 'role' => $this->role->value, + 'status' => $this->status->value, + 'has2FA' => $this->has2FA(), + 'meta' => $this->meta, + 'createdAt' => $this->createdAt?->format('c'), + 'updatedAt' => $this->updatedAt?->format('c'), + ]; + } + + public function __toString(): string + { + return sprintf('%s#%s<%s>', self::MODEL, $this->id, $this->role->value); + } + + // ---------------------------------------------------------- + // Helpers + // ---------------------------------------------------------- + + /** + * Converts 'email' data from an array into an Email object. + */ + private static function hydrateEmail(array $data): Email + { + $emailInput = $data['email'] ?? ''; + return $emailInput instanceof Email ? $emailInput : new Email($emailInput); + } + + /** + * Converts 'role' data from an array into a UserRole enum. + * Uses UserRole::tryFrom for safe conversion, defaulting to VIEWER. + */ + private static function hydrateRole(array $data): UserRole + { + $role = $data['role'] ?? null; + + if ($role instanceof UserRole) { + return $role; + } + + if (is_string($role) && $role !== '') { + return UserRole::tryFrom($role) ?? UserRole::VIEWER; + } + + return UserRole::VIEWER; + } + + /** + * Converts 'status' data from an array into a UserStatus enum. + * Uses UserStatus::tryFrom for safe conversion, defaulting to ACTIVE. + */ + private static function hydrateStatus(array $data): UserStatus + { + $status = $data['status'] ?? null; + + if ($status instanceof UserStatus) { + return $status; + } + + if (is_string($status) && $status !== '') { + return UserStatus::tryFrom($status) ?? UserStatus::ACTIVE; + } + + return UserStatus::ACTIVE; + } + + /** + * Converts a date string from an array into a DateTimeImmutable object. + * Returns null if the key is missing, empty, or the date is invalid. + */ + private static function hydrateDate(array $data, string $key): ?DateTimeImmutable + { + if (empty($data[$key])) { + return null; + } + + if ($data[$key] instanceof DateTimeImmutable) { + return $data[$key]; + } + + try { + return new DateTimeImmutable($data[$key]); + } catch (Exception $e) { + // Log error if needed: error_log("Failed to hydrate date '{$key}': {$e->getMessage()}"); + return null; + } + } + + /** + * Generates a random ID. + * Uses a static *variable* for caching the Randomizer, + * which is allowed in readonly classes. + */ + private static function generateId(): string + { + static $rng = null; + $rng ??= new Randomizer(); + + return bin2hex($rng->getBytes(8)); + } +} diff --git a/src/UserRole.php b/src/UserRole.php new file mode 100644 index 0000000..f1ceaef --- /dev/null +++ b/src/UserRole.php @@ -0,0 +1,62 @@ +with2FA('secret') + * ->promote(); + * + * echo $user->displayLabel(); // "Walmir Silva (editor)" + * ``` + * + * @package KaririCode\DevKit + * @category Query Filtering + * @author Walmir Silva + * @copyright 2025 KaririCode + * @license MIT + * @version 1.0.0 + * @since 1.0.0 + */ +enum UserRole: string +{ + case ADMIN = 'ADMIN'; + case EDITOR = 'EDITOR'; + case VIEWER = 'VIEWER'; + + public function canEdit(): bool + { + return match ($this) { + self::ADMIN, self::EDITOR => true, + self::VIEWER => false, + }; + } + + public function label(): string + { + return match ($this) { + self::ADMIN => 'admin', + self::EDITOR => 'editor', + self::VIEWER => 'viewer', + }; + } +} diff --git a/src/UserStatus.php b/src/UserStatus.php new file mode 100644 index 0000000..7d6fb9b --- /dev/null +++ b/src/UserStatus.php @@ -0,0 +1,29 @@ + + * @copyright 2025 KaririCode + * @license MIT + * @version 1.0.0 + * @since 1.0.0 + */ +enum UserStatus: string +{ + case ACTIVE = 'ACTIVE'; + case SUSPENDED = 'SUSPENDED'; + + public function isActive(): bool + { + return $this === self::ACTIVE; + } +} diff --git a/tests/Integration/UserProfileFlowTest.php b/tests/Integration/UserProfileFlowTest.php new file mode 100644 index 0000000..dd80e88 --- /dev/null +++ b/tests/Integration/UserProfileFlowTest.php @@ -0,0 +1,91 @@ + can edit. + * 3. Activates 2FA. + * 4. Verifies the final state and JSON serialization. + */ + #[Test] + public function itHandlesACompleteUserLifecycleFlow(): void + { + // --- 1) Simulate DB load (VIEWER + ACTIVE) --- + $dbData = [ + 'id' => 'flow-user-001', + 'name' => 'Utilizador de Fluxo', + 'email' => 'flow@exemplo.com', + 'role' => 'VIEWER', + 'status' => 'ACTIVE', + 'createdAt' => '2025-01-01T10:00:00+00:00', + 'updatedAt' => '2025-01-01T10:00:00+00:00', + ]; + + $user = UserProfile::fromArray($dbData); + + // Initial assertions + $this->assertSame(UserRole::VIEWER, $user->role); + $this->assertSame(UserStatus::ACTIVE, $user->status); + $this->assertFalse($user->has2FA()); + $this->assertFalse($user->canEdit(), 'Viewer ativo não deve poder editar.'); + + // --- 2) Business logic: promote (VIEWER -> EDITOR). Now can edit. --- + usleep(10); + $promotedUser = $user->promote(); + + $this->assertNotSame($user, $promotedUser); // immutable + $this->assertSame(UserRole::VIEWER, $user->role); // original intact + $this->assertSame(UserRole::EDITOR, $promotedUser->role); + $this->assertSame(UserStatus::ACTIVE, $promotedUser->status); + $this->assertTrue($promotedUser->canEdit(), 'Editor ativo deve poder editar.'); + $this->assertNotEquals($user->updatedAt, $promotedUser->updatedAt); + + // --- 3) Business logic: enable 2FA (still EDITOR + ACTIVE). --- + usleep(10); + $secureUser = $promotedUser->with2FA('segredo-super-secreto-123'); + + $this->assertNotSame($promotedUser, $secureUser); // immutable + $this->assertFalse($promotedUser->has2FA()); // previous intact + $this->assertTrue($secureUser->has2FA()); + $this->assertSame(UserRole::EDITOR, $secureUser->role); + $this->assertSame(UserStatus::ACTIVE, $secureUser->status); + $this->assertTrue($secureUser->canEdit(), 'Editor ativo deve poder editar.'); + $this->assertNotEquals($promotedUser->updatedAt, $secureUser->updatedAt); + + // --- 4) Final state + serialization checks --- + $finalJson = $secureUser->jsonSerialize(); + + $this->assertSame('UserProfile', $finalJson['model']); + $this->assertSame('flow-user-001', $finalJson['id']); + $this->assertSame('Utilizador de Fluxo', $finalJson['name']); + $this->assertSame('EDITOR', $finalJson['role']); + $this->assertSame('ACTIVE', $finalJson['status']); + $this->assertTrue($finalJson['has2FA']); + $this->assertSame('2025-01-01T10:00:00+00:00', $finalJson['createdAt']); + + // updatedAt must be newer than createdAt + $this->assertGreaterThan( + new DateTimeImmutable($finalJson['createdAt']), + new DateTimeImmutable($finalJson['updatedAt']) + ); + } +} diff --git a/tests/Unit/EnumsTest.php b/tests/Unit/EnumsTest.php new file mode 100644 index 0000000..89d0848 --- /dev/null +++ b/tests/Unit/EnumsTest.php @@ -0,0 +1,65 @@ + + */ + public static function roleEditProvider(): array + { + return [ + 'Admin pode editar' => [UserRole::ADMIN, true], + 'Editor pode editar' => [UserRole::EDITOR, true], + 'Viewer não pode editar' => [UserRole::VIEWER, false], + ]; + } + + /** + * Tests the edit permission logic. + */ + #[Test] + #[DataProvider('roleEditProvider')] + public function userRoleCanEdit(UserRole $role, bool $expectedResult): void + { + $this->assertSame($expectedResult, $role->canEdit()); + } + + /** + * Tests the labels of the roles. + */ + #[Test] + public function userRoleLabels(): void + { + $this->assertSame('admin', UserRole::ADMIN->label()); + $this->assertSame('editor', UserRole::EDITOR->label()); + $this->assertSame('viewer', UserRole::VIEWER->label()); + } + + /** + * Tests the 'active' status logic. + */ + #[Test] + public function userStatusIsActive(): void + { + $this->assertTrue(UserStatus::ACTIVE->isActive()); + $this->assertFalse(UserStatus::SUSPENDED->isActive()); + } +} diff --git a/tests/Unit/UserProfileTest.php b/tests/Unit/UserProfileTest.php new file mode 100644 index 0000000..076432b --- /dev/null +++ b/tests/Unit/UserProfileTest.php @@ -0,0 +1,239 @@ +assertInstanceOf(UserProfile::class, $user); + $this->assertSame('Utilizador Teste', $user->name); + $this->assertSame('teste@exemplo.com', (string) $user->email); + $this->assertSame(UserRole::EDITOR, $user->role); // Verifies the role + $this->assertSame(UserStatus::ACTIVE, $user->status); // Verifies the default status + $this->assertNotNull($user->id); + $this->assertNotNull($user->createdAt); + $this->assertNotNull($user->updatedAt); + $this->assertNull($user->twoFactorSecret); + } + + /** + * Tests creation from an array. + */ + #[Test] + public function itHydratesFromArray(): void + { + $expectedCreated = new DateTimeImmutable('2025-10-22T22:03:20+00:00'); + $data = [ + 'id' => 'user-123', + 'name' => 'Nome do Array', + 'email' => 'array@exemplo.com', + 'role' => 'ADMIN', + 'status' => 'SUSPENDED', + 'twoFactorSecret' => 'segredo123', + 'meta' => ['key' => 'value'], + 'createdAt' => $expectedCreated->format('c'), + ]; + + $user = UserProfile::fromArray($data); + + $this->assertSame('user-123', $user->id); + $this->assertSame('Nome do Array', $user->name); + $this->assertSame('array@exemplo.com', (string) $user->email); + $this->assertSame(UserRole::ADMIN, $user->role); + $this->assertSame(UserStatus::SUSPENDED, $user->status); + $this->assertSame('segredo123', $user->twoFactorSecret); + $this->assertSame(['key' => 'value'], $user->meta); + $this->assertSame( + $expectedCreated->format('Y-m-d\TH:i:sP'), + $user->createdAt?->format('Y-m-d\TH:i:sP') + ); + + $this->assertNull($user->updatedAt); + } + + /** + * Tests default values when hydrating with minimal data. + */ + #[Test] + public function itHydratesFromArrayWithDefaults(): void + { + $data = [ + 'name' => 'Utilizador Mínimo', + 'email' => 'minimo@exemplo.com', + ]; + + $user = UserProfile::fromArray($data); + + $this->assertNotNull($user->id); // ID is generated + $this->assertSame('Utilizador Mínimo', $user->name); + $this->assertSame(UserRole::VIEWER, $user->role); // Default role + $this->assertSame(UserStatus::ACTIVE, $user->status); // Default status + $this->assertNull($user->createdAt); + } + + /** + * Tests if 'fromArray' fails with an invalid email. + */ + #[Test] + public function itFailsHydrationWithInvalidEmail(): void + { + $data = [ + 'name' => 'Utilizador Falhado', + 'email' => 'email-invalido', + ]; + + // The exception comes from the Email constructor + $this->expectException(InvalidArgumentException::class); + UserProfile::fromArray($data); + } + + /** + * Tests 'with*' methods to ensure immutability. + * The UserProfile class is 'readonly', but 'with*' methods + * create NEW instances. + */ + #[Test] + public function itIsImmutableAndCreatesNewInstances(): void + { + $user1 = UserProfile::new('Original', 'original@exemplo.com'); + $user1CreatedAt = $user1->createdAt; + + // A microsecond delay is needed to ensure the timestamp changes + usleep(10); + + // 1. Promote + $user2 = $user1->promote(); + $this->assertNotSame($user1, $user2); // They are different objects + $this->assertSame(UserRole::VIEWER, $user1->role); // Original does not change + $this->assertSame(UserRole::EDITOR, $user2->role); // New object changes + $this->assertNotEquals($user1->updatedAt, $user2->updatedAt); + $this->assertEquals($user1CreatedAt, $user2->createdAt); // createdAt is maintained + + // 2. Suspend + $user3 = $user2->suspend(); + $this->assertNotSame($user2, $user3); + $this->assertSame(UserStatus::ACTIVE, $user2->status); // Previous does not change + $this->assertSame(UserStatus::SUSPENDED, $user3->status); // New object changes + + // 3. Add 2FA + $user4 = $user3->with2FA('segredo'); + $this->assertNotSame($user3, $user4); + $this->assertNull($user3->twoFactorSecret); // Previous does not change + $this->assertSame('segredo', $user4->twoFactorSecret); + $this->assertTrue($user4->has2FA()); + + // 4. Add Meta + $user5 = $user4->withMeta(['foo' => 'bar']); + $this->assertNotSame($user4, $user5); + $this->assertNull($user4->meta); // Previous does not change + $this->assertSame(['foo' => 'bar'], $user5->meta); + } + + /** + * Testa a lógica de promoção (não deve promover quem já é admin). + */ + #[Test] + public function itPromotesCorrectly(): void + { + $viewer = UserProfile::new('Viewer', 'v@v.com', UserRole::VIEWER); + $editor = UserProfile::new('Editor', 'e@e.com', UserRole::EDITOR); + $admin = UserProfile::new('Admin', 'a@a.com', UserRole::ADMIN); + + // Viewer is promoted to Editor + $this->assertSame(UserRole::EDITOR, $viewer->promote()->role); + // Editor remains Editor + $this->assertSame(UserRole::EDITOR, $editor->promote()->role); + // Admin remains Admin + $this->assertSame(UserRole::ADMIN, $admin->promote()->role); + } + + /** + * Provides data for the 'canEdit' test. + */ + public static function editPermissionProvider(): array + { + return [ + // Role, Status, Expected + 'Admin Ativo' => [UserRole::ADMIN, UserStatus::ACTIVE, true], + 'Editor Ativo' => [UserRole::EDITOR, UserStatus::ACTIVE, true], + 'Viewer Ativo' => [UserRole::VIEWER, UserStatus::ACTIVE, false], + 'Admin Suspenso' => [UserRole::ADMIN, UserStatus::SUSPENDED, false], + 'Editor Suspenso' => [UserRole::EDITOR, UserStatus::SUSPENDED, false], + 'Viewer Suspenso' => [UserRole::VIEWER, UserStatus::SUSPENDED, false], + ]; + } + + /** + * Tests the 'canEdit' logic (combination of Role and Status). + */ + #[Test] + #[DataProvider('editPermissionProvider')] + public function itChecksEditPermissions(UserRole $role, UserStatus $status, bool $expected): void + { + // We use fromArray to "force" the status + $user = UserProfile::fromArray([ + 'name' => 'Teste Permissão', + 'email' => 'p@p.com', + 'role' => $role, + 'status' => $status, + ]); + + $this->assertSame($expected, $user->canEdit()); + } + + /** + * Tests serialization outputs. + */ + #[Test] + public function itSerializesCorrectly(): void + { + $user = UserProfile::new('Serializar', 's@s.com'); + $id = $user->id; + + // Test __toString + $this->assertSame("UserProfile#{$id}", (string) $user); + + // Teste displayLabel + $this->assertSame("Serializar (viewer)", $user->displayLabel()); + + // Teste jsonSerialize + $json = $user->jsonSerialize(); + + $this->assertSame('UserProfile', $json['model']); + $this->assertSame(1, $json['version']); + $this->assertSame($id, $json['id']); + $this->assertSame('Serializar', $json['name']); + $this->assertSame('s@s.com', $json['email']); + $this->assertSame('VIEWER', $json['role']); + $this->assertSame('ACTIVE', $json['status']); + $this->assertFalse($json['has2FA']); + $this->assertNotNull($json['createdAt']); + } +} diff --git a/tests/Unit/ValueObjectsTest.php b/tests/Unit/ValueObjectsTest.php new file mode 100644 index 0000000..e336a7d --- /dev/null +++ b/tests/Unit/ValueObjectsTest.php @@ -0,0 +1,74 @@ +assertSame('teste@exemplo.com', $email->value); + // Verify string casting and JSON serialization + $this->assertSame('teste@exemplo.com', (string) $email); + $this->assertSame('teste@exemplo.com', $email->jsonSerialize()); + } + + #[Test] + public function emailThrowsExceptionForInvalidValue(): void + { + // Expect an InvalidArgumentException to be thrown for an invalid email format + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Invalid email: email-invalido"); + + // Attempt to create an Email object with an invalid string, which should trigger the exception + new Email('email-invalido'); + } + + /** + * Tests that a UserId object is successfully created with a valid, non-empty ID string. + * It also verifies the __toString() and jsonSerialize() methods. + */ + #[Test] + public function userIdIsCreatedWithValidValue(): void + { + $id = new UserId('id-12345'); + $this->assertSame('id-12345', $id->value); + // Verify string casting and JSON serialization + $this->assertSame('id-12345', (string) $id); + $this->assertSame('id-12345', $id->jsonSerialize()); + } + + /** + * Tests that a UserId object throws an exception when initialized with an empty string. + */ + #[Test] + public function userIdThrowsExceptionForEmptyValue(): void + { + // Expect an InvalidArgumentException to be thrown for an empty user ID + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("User ID cannot be empty."); + + new UserId(''); // Isto deve falhar + } +} From a54ec295c951669f67b2b5c1ad49622d4811a40f Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Mon, 3 Nov 2025 17:34:08 -0300 Subject: [PATCH 5/7] feat(build): implement modular Makefile architecture with comprehensive documentation - Restructure Makefile system following SOLID principles and DRY methodology - Organize build system into semantic modules by responsibility: * core/: Shared variables and reusable functions * local/: Setup, QA, and development helpers * docker/: Containerized execution and Docker Compose management * pipeline/: CI/CD orchestration workflows - Add complete modular documentation suite: * MAKEFILE.md: Overview and quick start guide * MAKEFILE-setup.md: Dependency management and installation * MAKEFILE-qa.md: Testing, linting, and static analysis * MAKEFILE-pipeline.md: CI/CD pipelines and pre-commit hooks * MAKEFILE-helpers.md: Benchmarking and release management * MAKEFILE-docker.md: Docker commands and isolated execution * MAKEFILE-compose.md: Full-stack Docker Compose environment - Implement new capabilities: * Unified benchmark interface with baseline comparison (make bench) * Docker Compose lifecycle management (make up/down/restart) * Git hooks automation (make git-hooks-setup) * Modular CI/CD pipelines (make ci/ci-full/cd) - Maintain backward compatibility with all existing make targets - Each module follows single responsibility principle - Comprehensive troubleshooting guides and best practices included Refs: #documentation #architecture #devex --- .docs/MAKEFILE-compose.md | 1046 ++++++++++++++++++ .docs/MAKEFILE-docker.md | 816 ++++++++++++++ .docs/MAKEFILE-helpers.md | 1016 +++++++++++++++++ .docs/MAKEFILE-pipeline.md | 949 ++++++++++++++++ .docs/MAKEFILE-qa.md | 1279 ++++++++++++++++++++++ .docs/MAKEFILE-setup.md | 1006 +++++++++++++++++ .docs/MAKEFILE.md | 324 ++++++ .make/core/Makefile.functions.mk | 66 ++ .make/core/Makefile.variables.mk | 58 + .make/docker/Makefile.docker-compose.mk | 200 ++++ .make/docker/Makefile.docker-core.mk | 25 + .make/docker/Makefile.docker-image.mk | 41 + .make/docker/Makefile.docker-qa.mk | 64 ++ .make/docker/Makefile.docker-tools.mk | 71 ++ .make/local/Makefile.helpers.mk | 221 ++++ .make/local/Makefile.qa.mk | 125 +++ .make/local/Makefile.setup.mk | 109 ++ .make/pipeline/Makefile.orchestration.mk | 53 + Makefile | 398 ++----- 19 files changed, 7551 insertions(+), 316 deletions(-) create mode 100644 .docs/MAKEFILE-compose.md create mode 100644 .docs/MAKEFILE-docker.md create mode 100644 .docs/MAKEFILE-helpers.md create mode 100644 .docs/MAKEFILE-pipeline.md create mode 100644 .docs/MAKEFILE-qa.md create mode 100644 .docs/MAKEFILE-setup.md create mode 100644 .docs/MAKEFILE.md create mode 100644 .make/core/Makefile.functions.mk create mode 100644 .make/core/Makefile.variables.mk create mode 100644 .make/docker/Makefile.docker-compose.mk create mode 100644 .make/docker/Makefile.docker-core.mk create mode 100644 .make/docker/Makefile.docker-image.mk create mode 100644 .make/docker/Makefile.docker-qa.mk create mode 100644 .make/docker/Makefile.docker-tools.mk create mode 100644 .make/local/Makefile.helpers.mk create mode 100644 .make/local/Makefile.qa.mk create mode 100644 .make/local/Makefile.setup.mk create mode 100644 .make/pipeline/Makefile.orchestration.mk diff --git a/.docs/MAKEFILE-compose.md b/.docs/MAKEFILE-compose.md new file mode 100644 index 0000000..d782c68 --- /dev/null +++ b/.docs/MAKEFILE-compose.md @@ -0,0 +1,1046 @@ +
+ +# Docker Compose Management + +[![KaririCode](https://img.shields.io/badge/KaririCode-DevKit-6D00CC?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) +[![Docker Compose](https://img.shields.io/badge/Docker%20Compose-Full%20Stack-2496ED?style=for-the-badge&logo=docker)](https://docs.docker.com/compose/) +[![Services](https://img.shields.io/badge/Services-PHP%20%7C%20Redis%20%7C%20Memcached-00C853?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) + +**kariricode/devkit** - Professional development environment for KaririCode Framework + +Part of the [KaririCode Framework](https://kariricode.org) ecosystem + +[Main Documentation](MAKEFILE.md) | [GitHub Repository](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Environment Setup](#environment-setup) +3. [Lifecycle Management](#lifecycle-management) +4. [Service Monitoring](#service-monitoring) +5. [Container Interaction](#container-interaction) +6. [Configuration Management](#configuration-management) +7. [Troubleshooting](#troubleshooting) +8. [Advanced Workflows](#advanced-workflows) +9. [Production Considerations](#production-considerations) + +--- + +## Overview + +### Architecture +``` +┌─────────────────────────────────────────────┐ +│ Docker Compose Stack │ +├─────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌──────────────┐ │ +│ │ PHP-FPM + │◄────►│ Memcached │ │ +│ │ Nginx + │ │ (11211) │ │ +│ │ Redis │ └──────────────┘ │ +│ │ (80, 6379) │ │ +│ └─────────────┘ │ +│ │ │ +│ │ Volume Mount │ +│ ▼ │ +│ /var/www/html ◄────► ./ │ +│ │ +└─────────────────────────────────────────────┘ + │ + │ Bridge Network + │ kariricode_network + │ + ┌────▼────┐ + │ Host │ + │ Machine │ + └─────────┘ +``` + +### Services + +| Service | Image | Purpose | Ports | Health Check | +|---------|-------|---------|-------|--------------| +| **php** | kariricode/php-api-stack:dev | Application runtime | 80, 6379 | Process check | +| **memcached** | memcached:1.6-alpine | Memory caching | 11211 | netcat probe | + +### Features + +✅ **Live Code Sync**: Volume mounting for instant updates +✅ **Isolated Network**: Bridge network for service communication +✅ **Health Monitoring**: Built-in health checks +✅ **Xdebug Support**: Configurable debugging +✅ **Auto-restart**: Service recovery on failure +✅ **Environment Variables**: Flexible configuration via `.env` + +--- + +## Environment Setup + +### Initial Configuration + +#### Step 1: Create Environment File +```bash +# Auto-created on first 'make up', or manually: +cp .env.example .env +``` + +#### Step 2: Configure Variables +```bash +# Edit .env +nano .env +``` + +**Key Configuration Variables:** +```bash +# Application +APP_NAME=kariricode-devkit +APP_ENV=development +APP_DEBUG=true +APP_PORT=8089 # Host port → container:80 + +# Demo & Health Check +DEMO_MODE=false +HEALTH_CHECK_INSTALL=true + +# Cache Services +REDIS_PORT=63777 # Host port → container:6379 +MEMCACHED_PORT=11210 # Host port → container:11211 + +# Xdebug +XDEBUG_MODE=off # off/debug/coverage/profile +XDEBUG_CLIENT_HOST=host.docker.internal + +# Composer +COMPOSER_MEMORY_LIMIT=-1 +``` + +#### Step 3: Verify Configuration +```bash +# Check .env file +make env-check + +# Output: +# ╔════════════════════════════════════════════════════════╗ +# ✓ .env file exists +# APP_NAME=kariricode-devkit +# APP_PORT=8089 +# REDIS_PORT=63777 +# ... +# ╚════════════════════════════════════════════════════════╝ +``` + +### Port Conflict Resolution + +**Problem:** Port already in use +```bash +# 1. Identify conflicting process +sudo lsof -i :8089 +# COMMAND PID USER +# nginx 1234 root + +# 2. Option A: Stop conflicting service +sudo systemctl stop nginx + +# 3. Option B: Change port in .env +echo "APP_PORT=8090" >> .env + +# 4. Restart +make down && make up +``` + +**Common Port Conflicts:** + +| Port | Service | Solution | +|------|---------|----------| +| 8089 | Nginx/Apache | Change `APP_PORT` | +| 6379 | Redis | Change `REDIS_PORT` | +| 11211 | Memcached | Change `MEMCACHED_PORT` | + +--- + +## Lifecycle Management + +### Starting Services + +#### Standard Start +```bash +# Start all services in detached mode +make up + +# What happens: +# 1. Checks for .env (creates if missing) +# 2. Pulls images if needed +# 3. Creates network +# 4. Starts containers +# 5. Shows status + +# Output: +# ✓ Services started +# ╔════════════════════════════════════════════════════════╗ +# NAME STATUS PORTS +# kariricode-devkit_php Up 5 seconds 0.0.0.0:8089->80/tcp +# kariricode-devkit_memcached Up 5 seconds 0.0.0.0:11210->11211/tcp +# ╚════════════════════════════════════════════════════════╝ +``` + +#### Start with Build +```bash +# Force rebuild images before starting +make up-build + +# Use cases: +# - Dockerfile changes +# - Base image updates +# - Fresh start needed +``` + +### Stopping Services +```bash +# Stop and remove containers (preserves volumes) +make down +# ✓ Services stopped and removed + +# Stop without removing +make stop +# Containers remain, can be started with 'make start' + +# Start existing stopped containers +make start +``` + +### Restarting Services +```bash +# Restart all services +make restart + +# Equivalent to: +# docker compose restart + +# Use cases: +# - Configuration changes in docker-compose.yml +# - Memory leaks +# - Service recovery +# - .env changes (some variables) +``` + +### Complete Rebuild +```bash +# Nuclear option: destroy and recreate everything +make rebuild + +# Executes: +# 1. make down → Stop and remove containers +# 2. make clean-volumes → Delete ALL volumes +# 3. make up-build → Rebuild images and start + +# ⚠️ WARNING: This destroys ALL data! +# Are you sure? [y/N] +``` + +--- + +## Service Monitoring + +### Status Checks + +#### Current Status +```bash +# Show service status +make status +make ps # Alias + +# Output: +# ╔════════════════════════════════════════════════════════╗ +# NAME STATE STATUS +# kariricode-devkit_php Up healthy +# kariricode-devkit_memcached Up healthy +# ╚════════════════════════════════════════════════════════╝ +``` + +#### Health Checks +```bash +# Detailed health status +make health + +# Output shows: +# kariricode-devkit_php: healthy - Up 2 minutes +# kariricode-devkit_memcached: healthy - Up 2 minutes + +# Health check definitions: +# - Memcached: nc -z 127.0.0.1 11211 (every 10s) +# - PHP: Process running check +``` + +### Logs + +#### View Logs +```bash +# All services +make logs + +# Specific service +make logs SERVICE=php +make logs SERVICE=memcached + +# Example output: +# kariricode-devkit_php | [15-Jan-2025 10:30:00] NOTICE: fpm is running +# kariricode-devkit_memcached | <5 new connections +``` + +#### Follow Logs (Real-time) +```bash +# Tail all logs +make logs-follow + +# Tail specific service +make logs-follow SERVICE=php + +# Press Ctrl+C to stop following +``` + +**Log Use Cases:** + +| Scenario | Command | What to Look For | +|----------|---------|------------------| +| Application errors | `make logs-follow SERVICE=php` | PHP errors, warnings | +| Service crashes | `make logs` | Exit codes, error messages | +| Performance issues | `make logs SERVICE=php` | Slow query logs, timeouts | +| Cache debugging | `make logs SERVICE=memcached` | Connection stats, evictions | + +### Port Mappings +```bash +# Show exposed ports +make ports + +# Output: +# ╔════════════════════════════════════════════════════════╗ +# NAME PORTS +# *_php 0.0.0.0:8089->80/tcp, :::6379->6379/tcp +# *_memcached 0.0.0.0:11210->11211/tcp +# ╚════════════════════════════════════════════════════════╝ +``` + +**Access Services:** +```bash +# Application HTTP +curl http://localhost:8089 + +# Redis (from host, if exposed) +redis-cli -h localhost -p 63777 + +# Memcached (from host) +echo "stats" | nc localhost 11210 +``` + +--- + +## Container Interaction + +### PHP Container + +#### Interactive Shell +```bash +# Open bash shell +make exec-php +make shell # Alias + +# Inside container: +root@abc123:/var/www/html# php -v +root@abc123:/var/www/html# composer --version +root@abc123:/var/www/html# ls -la +``` + +#### Execute Commands +```bash +# Single command execution +make exec-php CMD="php -v" +# PHP 8.4.14 (cli) (built: Jan 15 2025) (NTS) + +make exec-php CMD="composer --version" +# Composer version 2.8.4 + +make exec-php CMD="php artisan migrate" +make exec-php CMD="./bin/console cache:clear" +``` + +**Common Tasks:** +```bash +# Check PHP modules +make exec-php CMD="php -m" + +# Test Redis connection +make exec-php CMD="php -r 'new Redis();'" + +# View logs +make exec-php CMD="tail -f var/log/app.log" + +# Run application commands +make exec-php CMD="make test" +``` + +### Memcached Container +```bash +# Connect to Memcached container +make exec-memcached + +# Inside container: +/ # echo "stats" | nc localhost 11211 +STAT pid 1 +STAT uptime 3600 +STAT curr_connections 5 +... + +/ # echo "flush_all" | nc localhost 11211 +OK +``` + +--- + +## Configuration Management + +### Validate Configuration + +#### Syntax Validation +```bash +# Check docker-compose.yml syntax +make validate-compose + +# Output: +# ✓ docker-compose.yml is valid + +# Errors show line numbers and details +``` + +#### View Resolved Configuration +```bash +# Show merged configuration +make config + +# Output includes: +# - Environment variable substitution +# - Service definitions +# - Network configuration +# - Volume mounts +# - All resolved values + +# Useful for debugging: +# - Environment variable issues +# - Configuration inheritance +# - Overrides from .env +``` + +### Environment Verification +```bash +# Check .env file status +make env-check + +# Shows: +# 1. File existence +# 2. First 20 variables +# 3. Validation warnings + +# If missing: +# ✗ .env file not found +# Run: cp .env.example .env +``` + +### Network Inspection +```bash +# Inspect Docker network +make network-inspect + +# Output: +# ╔════════════════════════════════════════════════════════╗ +# [ +# { +# "Name": "kariricode-devkit_network", +# "Driver": "bridge", +# "Scope": "local", +# "Subnet": "172.20.0.0/16", +# "Gateway": "172.20.0.1", +# "Containers": { +# "php": {"IPv4Address": "172.20.0.2"}, +# "memcached": {"IPv4Address": "172.20.0.3"} +# } +# } +# ] +# ╚════════════════════════════════════════════════════════╝ +``` + +--- + +## Composer Integration + +### Install Dependencies +```bash +# Install from composer.lock +make composer-install + +# Executes in PHP container: +# composer install --no-interaction --prefer-dist --optimize-autoloader +``` + +### Update Dependencies +```bash +# Update all dependencies +make composer-update + +# Executes: +# composer update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader +``` + +**Advantages of Container Execution:** +- ✅ No local PHP version conflicts +- ✅ Consistent dependencies across team +- ✅ Matches production environment +- ✅ Isolated from host system + +--- + +## Troubleshooting + +### Issue 1: Services Won't Start + +**Symptoms:** +- Container exits immediately +- `make up` fails + +**Diagnosis:** +```bash +# Check logs +make logs + +# Common error messages: +# - "Address already in use" → Port conflict +# - "Invalid reference format" → .env syntax error +# - "Pull access denied" → Image not found +``` + +**Solutions:** +```bash +# Port conflict +echo "APP_PORT=8090" >> .env +make down && make up + +# Invalid .env +make env-check +# Fix syntax errors + +# Missing image +make docker-pull +``` + +### Issue 2: Can't Connect to Services + +**Symptoms:** +- `Connection refused` +- `curl: (7) Failed to connect` + +**Diagnosis:** +```bash +# Check if services are running +make status + +# Check ports +make ports + +# Check network +make network-inspect +``` + +**Solutions:** +```bash +# Service not running +make restart + +# Wrong port +curl http://localhost:$(grep APP_PORT .env | cut -d= -f2) + +# Network issue +make down +docker network prune +make up +``` + +### Issue 3: Permission Denied + +**Symptoms:** +- `mkdir: cannot create directory: Permission denied` +- `composer: Failed to download` + +**Diagnosis:** +```bash +# Check file ownership +make exec-php CMD="ls -la /var/www/html" +``` + +**Solution:** +```bash +# Fix ownership on host +sudo chown -R $USER:$USER . + +# Or fix inside container +make exec-php CMD="chown -R www-data:www-data /var/www/html" + +# Restart +make restart +``` + +### Issue 4: Volume Data Corruption + +**Symptoms:** +- Stale data +- Inconsistent state +- Old files persist + +**Solution:** +```bash +# Nuclear option: destroy volumes +make clean-volumes + +# ⚠️ WARNING: Confirmation required +# This will delete ALL volume data! +# Are you sure? [y/N] y + +# ✓ Volumes removed + +# Rebuild +make up-build +``` + +### Issue 5: Xdebug Not Working + +**Diagnosis:** +```bash +# Check Xdebug is loaded +make exec-php CMD="php -v" +# Should show: with Xdebug v3.x + +# Check mode +make exec-php CMD="php -r 'echo ini_get(\"xdebug.mode\");'" +``` + +**Solution:** +```bash +# Enable in .env +sed -i 's/XDEBUG_MODE=off/XDEBUG_MODE=debug/' .env + +# Restart services +make restart + +# Verify +make exec-php CMD="php -m | grep xdebug" +``` + +--- + +## Advanced Workflows + +### Development with Live Reload +```bash +# 1. Start services +make up + +# 2. Open separate terminal for logs +make logs-follow SERVICE=php + +# 3. Edit code in your IDE +# Changes are immediately visible (volume mount) + +# 4. Test changes +curl http://localhost:8089/health + +# 5. No restart needed! +``` + +### Multi-Environment Configuration + +#### Development Environment +```bash +# Use .env.development +cp .env.development .env +make up + +# Characteristics: +# - XDEBUG_MODE=debug +# - APP_DEBUG=true +# - Verbose logging +``` + +#### Testing Environment +```bash +# Use .env.testing +cp .env.testing .env +make rebuild + +# Characteristics: +# - XDEBUG_MODE=coverage +# - Clean state +# - Test database +``` + +#### Production Simulation +```bash +# Use .env.production +cp .env.production .env +make up-build + +# Characteristics: +# - XDEBUG_MODE=off +# - APP_DEBUG=false +# - Optimized settings +``` + +### Debugging Workflows + +#### Xdebug with IDE + +**Step 1: Configure .env** +```bash +echo "XDEBUG_MODE=debug" > .env +echo "XDEBUG_CLIENT_HOST=host.docker.internal" >> .env +make restart +``` + +**Step 2: IDE Configuration (PHPStorm)** +``` +Settings → PHP → Servers: +- Name: kariricode-devkit +- Host: localhost +- Port: 8089 +- Path mappings: ./ → /var/www/html +``` + +**Step 3: Start Listening** +``` +PHPStorm: Run → Start Listening for PHP Debug Connections +``` + +**Step 4: Trigger Breakpoint** +```bash +curl http://localhost:8089/debug +``` + +#### Performance Profiling +```bash +# Enable profiling +sed -i 's/XDEBUG_MODE=off/XDEBUG_MODE=profile/' .env +make restart + +# Run application +curl http://localhost:8089/api/endpoint + +# View profile files +make exec-php CMD="ls -lh /tmp/xdebug" +# cachegrind.out.XXXX files + +# Download for analysis +docker cp kariricode-devkit_php:/tmp/xdebug/cachegrind.out.1234 ./ +``` + +**Analyze with:** +- **KCacheGrind** (Linux) +- **QCacheGrind** (macOS/Windows) +- **Webgrind** (Web-based) + +### Integration Testing Workflow +```bash +# 1. Start full stack +make up + +# 2. Wait for services +sleep 3 + +# 3. Run integration tests +make test-compose + +# Workflow in make test-compose: +# - Verifies services are up +# - Executes: docker compose exec php make test +# - Returns test exit code + +# 4. View logs on failure +make logs + +# 5. Cleanup +make down +``` + +--- + +## Production Considerations + +### ⚠️ Development Only + +This Docker Compose setup is **NOT production-ready**. It's designed for: +- ✅ Local development +- ✅ Integration testing +- ✅ Rapid prototyping +- ✅ Team onboarding + +### Production Limitations + +| Concern | Development Setup | Production Requirement | +|---------|-------------------|------------------------| +| **SSL/TLS** | No HTTPS | Terminate SSL at load balancer | +| **Secrets** | .env file | Vault/Secrets Manager | +| **Scaling** | Single container | Horizontal scaling, orchestration | +| **Monitoring** | Docker logs | Prometheus, Grafana, ELK | +| **High Availability** | No redundancy | Multi-node, failover | +| **Resource Limits** | Unlimited | CPU/Memory constraints | +| **Backup** | No strategy | Automated backups, replication | +| **Security** | Debug enabled | Hardened images, least privilege | + +### Production Recommendations + +**For Production Deployment:** + +1. **Use Orchestration Platforms** +``` + - Kubernetes (EKS, GKE, AKS) + - Docker Swarm (simple cases) + - AWS ECS/Fargate +``` + +2. **Separate Stateful Services** +``` + - Managed Redis (AWS ElastiCache, Redis Cloud) + - Managed Memcached + - External databases +``` + +3. **Implement Security Best Practices** +``` + - Run as non-root user + - Scan images for vulnerabilities + - Use minimal base images (Alpine) + - Regular security updates +``` + +4. **Add Monitoring & Logging** +``` + - Centralized logging (ELK, Loki) + - Metrics collection (Prometheus) + - Distributed tracing (Jaeger) + - Alerting (PagerDuty, Opsgenie) +``` + +5. **Implement CI/CD** +``` + - Automated testing + - Image scanning + - Deployment automation + - Rollback capabilities +``` + +--- + +## CI/CD Integration + +### GitHub Actions Example +```yaml +name: Integration Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Create .env + run: cp .env.example .env + + - name: Start Docker Compose + run: make up + + - name: Wait for services + run: sleep 5 + + - name: Run tests + run: make test-compose + + - name: Show logs on failure + if: failure() + run: make logs + + - name: Cleanup + if: always() + run: make down +``` + +### GitLab CI Example +```yaml +integration-tests: + stage: test + image: docker:latest + services: + - docker:dind + + variables: + DOCKER_DRIVER: overlay2 + + before_script: + - apk add --no-cache make + - cp .env.example .env + - make up + + script: + - make test-compose + + after_script: + - make logs + - make down + + artifacts: + when: always + reports: + junit: build/reports/junit.xml + paths: + - coverage/ +``` + +--- + +## Command Reference + +### Lifecycle +```bash +make up # Start services +make up-build # Start with build +make down # Stop and remove +make stop # Stop (preserve containers) +make start # Start existing containers +make restart # Restart services +make rebuild # Complete rebuild +``` + +### Monitoring +```bash +make status # Service status +make ps # Alias for status +make health # Health checks +make logs # View logs +make logs-follow # Follow logs (real-time) +make ports # Show port mappings +``` + +### Interaction +```bash +make exec-php # PHP shell +make shell # Alias for exec-php +make exec-memcached # Memcached shell +make composer-install # Install dependencies +make composer-update # Update dependencies +make test-compose # Run tests in compose +``` + +### Configuration +```bash +make config # View resolved config +make validate-compose # Validate syntax +make env-check # Check .env file +make network-inspect # Inspect network +make inspect-php # PHP container details +``` + +### Cleanup +```bash +make prune # Remove unused resources +make clean-volumes # Delete volumes (destructive) +``` + +--- + +## Best Practices + +### 1. Use .env for Configuration +```bash +# ❌ Bad: Hardcode in docker-compose.yml +ports: + - "8089:80" + +# ✅ Good: Use environment variables +ports: + - "${APP_PORT:-8089}:80" +``` + +### 2. Clean Up Regularly +```bash +# Weekly cleanup +make prune + +# Before major changes +make rebuild +``` + +### 3. Monitor Health +```bash +# Add to daily routine +make health + +# Investigate unhealthy services immediately +make logs SERVICE= +``` + +### 4. Version Control +```bash +# ✅ Commit +- docker-compose.yml +- .env.example +- Makefile + +# ❌ Don't commit +- .env +- Volumes +- Build artifacts +``` + +### 5. Document Custom Changes +```yaml +# docker-compose.yml +services: + php: + # Custom: Added for X feature (see issue #123) + environment: + CUSTOM_VAR: value +``` + +--- + +## Quick Reference Card +``` +╔═══════════════════════════════════════════════════════════╗ +║ Docker Compose Quick Reference ║ +╠═══════════════════════════════════════════════════════════╣ +║ START │ make up ║ +║ STOP │ make down ║ +║ RESTART │ make restart ║ +║ REBUILD │ make rebuild ║ +║──────────────┼────────────────────────────────────────────║ +║ STATUS │ make status ║ +║ LOGS │ make logs-follow ║ +║ HEALTH │ make health ║ +║──────────────┼────────────────────────────────────────────║ +║ SHELL │ make shell ║ +║ COMMAND │ make exec-php CMD="php -v" ║ +║ COMPOSE TEST │ make test-compose ║ +║──────────────┼────────────────────────────────────────────║ +║ VALIDATE │ make validate-compose ║ +║ CHECK ENV │ make env-check ║ +║ INSPECT │ make inspect-php ║ +╚═══════════════════════════════════════════════════════════╝ +``` + +--- + +**Version**: 1.0.0 +**Module**: `Makefile.docker-compose.mk` +**Maintainer**: Walmir Silva diff --git a/.docs/MAKEFILE-docker.md b/.docs/MAKEFILE-docker.md new file mode 100644 index 0000000..ea80664 --- /dev/null +++ b/.docs/MAKEFILE-docker.md @@ -0,0 +1,816 @@ +
+ +# Docker Commands + +[![KaririCode](https://img.shields.io/badge/KaririCode-DevKit-6D00CC?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) +[![Docker](https://img.shields.io/badge/Docker-Containerized-2496ED?style=for-the-badge&logo=docker)](https://www.docker.com) +[![PHP Stack](https://img.shields.io/badge/PHP%20Stack-8.4-777BB4?style=for-the-badge&logo=php)](https://hub.docker.com/r/kariricode/php-api-stack) + +**kariricode/devkit** - Professional development environment for KaririCode Framework + +Part of the [KaririCode Framework](https://kariricode.org) ecosystem + +[Main Documentation](MAKEFILE.md) | [GitHub Repository](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Docker Infrastructure](#docker-infrastructure) +3. [Docker Core Commands](#docker-core-commands) +4. [Docker QA Pipeline](#docker-qa-pipeline) +5. [Docker Image Management](#docker-image-management) +6. [Docker Utility Tools](#docker-utility-tools) +7. [Troubleshooting](#troubleshooting) +8. [Best Practices](#best-practices) + +--- +## Overview + +### Scope + +The Docker modules provide containerized execution for: +- **Isolated Environment**: Consistent PHP 8.4 environment +- **CI/CD Simulation**: Match production conditions locally +- **Team Consistency**: Same environment across all developers +- **Clean State**: No local configuration interference + +### Module Architecture +``` +Docker Modules (.make/docker/) +│ +├── Makefile.docker-core.mk → Shell, Composer, PHP execution +├── Makefile.docker-compose.mk → Full stack management (see MAKEFILE-compose.md) +├── Makefile.docker-qa.mk → QA pipeline in containers +├── Makefile.docker-image.mk → Image management +└── Makefile.docker-tools.mk → Utility tools +``` + +### Docker vs Local Execution + +| Aspect | Local | Docker | +|--------|-------|--------| +| **Speed** | ⚡ Fastest | 🐢 Slower (container overhead) | +| **Consistency** | ⚠️ Varies by environment | ✅ Always consistent | +| **IDE Integration** | ✅ Full support | ⚠️ Requires configuration | +| **Debugging** | ✅ Native Xdebug | ⚠️ Network setup needed | +| **CI/CD Match** | ❌ May differ | ✅ Identical | +| **Team Sync** | ❌ Varies | ✅ Identical | +| **Clean State** | ❌ Cached configs | ✅ Fresh each run | + +**When to Use Docker:** +- ✅ CI/CD pipelines (mandatory) +- ✅ Pre-commit final validation +- ✅ Testing specific PHP versions +- ✅ New team member onboarding +- ✅ Isolating global configuration + +**When to Use Local:** +- ✅ Daily development +- ✅ IDE integration (debugging, intellisense) +- ✅ Fast iteration cycles +- ✅ Custom Xdebug configuration + +--- + +## Docker Infrastructure + +### Container Image +```bash +# Image details +Image: kariricode/php-api-stack:dev +Base: php:8.4-fpm-alpine +Size: ~350MB +Registry: Docker Hub + +# Included tools: +- PHP 8.4.14 (CLI + FPM) +- Composer 2.8.4 +- Xdebug 3.x +- Git +- Make +- Common PHP extensions +``` + +### Volume Mounting +```bash +# Project directory mounted at: +Host: $(PWD) +Container: /var/www/html + +# Live sync: Changes immediately visible in container +``` + +### Network Configuration +```bash +# Default: Host network mode +- No port mapping needed for basic commands +- Container can access host services + +# Compose: Bridge network +- Isolated network for services +- See MAKEFILE-compose.md for details +``` + +--- + +## Docker Core Commands + +### Interactive Shell + +#### Open Bash Shell +```bash +make docker-shell + +# Output: +# → Opening Docker shell (kariricode/php-api-stack:dev)... +# +# root@abc123:/var/www/html# + +# Inside container: +root@abc123:/var/www/html# php -v +PHP 8.4.14 (cli) (built: Jan 15 2025) (NTS) + +root@abc123:/var/www/html# ls -la +total 120 +drwxr-xr-x 8 root root 4096 Jan 15 10:30 . +drwxr-xr-x 1 root root 4096 Jan 15 10:20 .. +drwxr-xr-x 3 root root 4096 Jan 15 10:25 src +drwxr-xr-x 3 root root 4096 Jan 15 10:25 tests +-rw-r--r-- 1 root root 2456 Jan 15 10:22 composer.json +... + +root@abc123:/var/www/html# exit +``` + +#### Use Cases +```bash +# Explore environment +make docker-shell +> php -m # Check modules +> php -i | grep xdebug # Check Xdebug +> composer --version # Verify Composer + +# Debug issues +make docker-shell +> ls -la vendor/ # Check dependencies +> php -l src/File.php # Lint specific file + +# Run custom commands +make docker-shell +> phpunit --filter testSpecificMethod +> php bin/console custom:command +``` + +### Composer Commands +```bash +# Generic Composer execution +make docker-composer CMD="" + +# Examples: + +# Install dependencies +make docker-composer CMD="install" + +# Update dependencies +make docker-composer CMD="update" + +# Show installed packages +make docker-composer CMD="show --installed" + +# Require new package +make docker-composer CMD="require kariricode/new-component" + +# Remove package +make docker-composer CMD="remove vendor/package" + +# Validate composer.json +make docker-composer CMD="validate --strict" + +# Diagnose issues +make docker-composer CMD="diagnose" +``` + +### PHP Commands +```bash +# Generic PHP execution +make docker-php CMD="" + +# Examples: + +# Check PHP version +make docker-php CMD="-v" + +# Show PHP info +make docker-php CMD="-i" + +# List loaded modules +make docker-php CMD="-m" + +# Check configuration +make docker-php CMD="-i | grep xdebug" + +# Execute script +make docker-php CMD="script.php" + +# Evaluate expression +make docker-php CMD="-r 'echo PHP_VERSION;'" +``` + +--- + +## Docker QA Pipeline + +### Individual QA Commands + +All local QA commands have Docker equivalents: +```bash +# Testing +make docker-test # All tests +make docker-test-unit # Unit tests only +make docker-test-integration # Integration tests + +# Static Analysis +make docker-phpstan # PHPStan +make docker-psalm # Psalm +make docker-analyse # All analysis + +# Code Style +make docker-cs-check # Check style +make docker-format # Auto-fix style +make docker-lint # Syntax check + +# Coverage & Mutation +make docker-coverage # Code coverage +make docker-mutation # Mutation testing + +# Benchmarks +make docker-bench # Performance benchmarks +``` + +### Docker CI Pipelines + +#### Fast CI in Docker +```bash +make docker-ci + +# Executes in isolated container: +# ╔════════════════════════════════════════════════════════╗ +# ║ Docker CI Pipeline (Isolated Environment) ║ +# ╚════════════════════════════════════════════════════════╝ +# +# → Running make ci in Docker... +# [Complete CI pipeline output...] +# ✓ Docker make ci complete +# +# ✓ Docker CI pipeline completed + +# Advantages: +# - No local config interference +# - Same environment as production +# - Clean state every run +``` + +#### Full CI in Docker +```bash +make docker-ci-full + +# Complete quality validation in container: +# ╔════════════════════════════════════════════════════════╗ +# ║ Docker Full CI Pipeline (Isolated Environment) ║ +# ╚════════════════════════════════════════════════════════╝ +# +# → Running make ci-full in Docker... +# [Full CI pipeline with coverage & mutation...] +# ✓ Docker make ci-full complete +# +# ✓ Docker full CI pipeline completed +``` + +### Comparison: Local vs Docker CI +```bash +# Local CI (fast, uses your PHP config) +make ci +# Time: ~1-2 minutes +# Uses: Local PHP, local cache + +# Docker CI (isolated, production-like) +make docker-ci +# Time: ~2-3 minutes +# Uses: Container PHP, no cache interference + +# Use case: +# - Daily: make ci (fast feedback) +# - Pre-push: make docker-ci (final validation) +``` + +--- + +## Docker Image Management + +### Pull Image +```bash +make docker-pull + +# Downloads latest image from Docker Hub: +# → Pulling Docker image kariricode/php-api-stack:dev... +# dev: Pulling from kariricode/php-api-stack +# a1234567890b: Pull complete +# b2345678901c: Pull complete +# ... +# Status: Downloaded newer image for kariricode/php-api-stack:dev +# ✓ Docker image pulled +``` + +### Docker Environment Info +```bash +make docker-info + +# Comprehensive environment details: +# ╔════════════════════════════════════════════════════════╗ +# ║ Docker Environment Information ║ +# ╚════════════════════════════════════════════════════════╝ +# +# Docker Image: kariricode/php-api-stack:dev +# Mount Point: /home/user/project:/app +# +# ╔════════════════════════════════════════════════════════╗ +# ║ Container PHP Info ║ +# ╚════════════════════════════════════════════════════════╝ +# +# PHP 8.4.14 (cli) (built: Jan 15 2025 12:34:56) (NTS) +# Copyright (c) The PHP Group +# Zend Engine v4.4.14 +# +# Composer version 2.8.4 2024-12-12 +``` + +### Clean Docker Resources +```bash +make docker-clean + +# Removes unused Docker resources: +# → Cleaning Docker resources... +# Deleted Containers: +# abc123def456 +# +# Deleted Images: +# untagged: old-image:tag +# +# Total reclaimed space: 1.5GB +# ✓ Docker cleanup complete +``` + +--- + +## Docker Utility Tools + +### Text Processing + +#### JSON Processing (jq) +```bash +# Extract data from JSON files +make docker-jq CMD="'.version' composer.json" + +# Output: +# → Running jq '.version' composer.json in Docker... +# "2.0.0" + +# More examples: +make docker-jq CMD="'.require' composer.json" +make docker-jq CMD="'.authors[0].name' composer.json" +make docker-jq CMD="keys composer.json" +``` + +#### YAML Processing (yq) +```bash +# Parse YAML files +make docker-yq CMD="'.services' docker-compose.yml" + +# Output: +# → Running yq '.services' docker-compose.yml in Docker... +# php: +# image: kariricode/php-api-stack:dev +# ... + +# More examples: +make docker-yq CMD="'.services.php.image' docker-compose.yml" +``` + +### File Viewing & Editing + +#### View Files (less) +```bash +# View file contents +make docker-less CMD="composer.json" + +# Opens less viewer in container +# Press 'q' to quit +``` + +#### Edit Files (vim) +```bash +# Edit files with vim +make docker-vim CMD="src/Parser.php" + +# Opens vim editor in container +# :wq to save and quit +# :q! to quit without saving +``` + +**Note**: File paths relative to `/var/www/html` (container working directory) + +### System Diagnostics + +#### List Open Files (lsof) +```bash +# Check open ports +make docker-lsof CMD="-i" + +# Check specific port +make docker-lsof CMD="-i :8080" + +# Check open files by process +make docker-lsof CMD="-p 1234" +``` + +**Requires**: `--cap-add=SYS_PTRACE` (automatically added by Makefile) + +#### Trace System Calls (strace) +```bash +# Trace PHP execution +make docker-strace CMD="php -v" + +# Output shows system calls: +# execve("/usr/local/bin/php", ["php", "-v"], ...) +# brk(NULL) +# ... + +# Trace script execution +make docker-strace CMD="php script.php" +``` + +**Requires**: `--cap-add=SYS_PTRACE` (automatically added) + +### Network Diagnostics + +#### IP Configuration +```bash +# Show network interfaces +make docker-ip CMD="addr" + +# Output: +# 1: lo: mtu 65536 +# inet 127.0.0.1/8 +# 2: eth0: mtu 1500 +# inet 172.17.0.2/16 + +# Show routing table +make docker-ip CMD="route" +``` + +#### Network Connectivity (netcat) +```bash +# Test port connectivity +make docker-nc CMD="-vz localhost 9000" + +# Output: +# localhost [127.0.0.1] 9000 (?) open + +# Listen on port +make docker-nc CMD="-l 8080" + +# Connect to service +make docker-nc CMD="redis 6379" +``` + +--- + +## Troubleshooting + +### Issue 1: Container Exits Immediately + +**Symptoms:** +```bash +$ make docker-shell +→ Opening Docker shell... +# (exits immediately) +``` + +**Diagnosis:** +```bash +# Check Docker daemon +docker ps + +# Check image integrity +docker images | grep kariricode +``` + +**Solutions:** +```bash +# Pull fresh image +make docker-pull + +# Verify image +docker run --rm kariricode/php-api-stack:dev php -v + +# Check Docker daemon +sudo systemctl status docker +``` + +### Issue 2: Permission Denied Errors + +**Symptoms:** +```bash +$ make docker-composer CMD="install" +Permission denied: composer.lock +``` + +**Cause:** User ID mismatch between host and container + +**Solutions:** + +**Option A: Fix Ownership** +```bash +# On host +sudo chown -R $USER:$USER . + +# Or specific files +sudo chown $USER composer.lock +``` + +**Option B: Run as Non-root** +```bash +# Edit Makefile.docker-core.mk +DOCKER_RUN := docker run --rm \ + -v $(PWD):/var/www/html \ + -w /var/www/html \ + -u $(shell id -u):$(shell id -g) \ # Add this line + kariricode/php-api-stack:dev +``` + +### Issue 3: Xdebug Not Working + +**Symptoms:** +```bash +$ make docker-test +# Tests run but no coverage data +``` + +**Diagnosis:** +```bash +make docker-php CMD="-m | grep xdebug" +# If empty, Xdebug not loaded +``` + +**Solutions:** +```bash +# Check Xdebug configuration +make docker-shell +> php -i | grep xdebug.mode +# Should show: xdebug.mode => coverage + +# If not, configure in docker-compose.yml +# See MAKEFILE-compose.md for Xdebug setup +``` + +### Issue 4: Slow Docker Performance + +**Symptoms:** +- Commands take significantly longer in Docker +- File operations are slow + +**Solutions:** + +**Linux: Use Docker Native** +```bash +# Already optimal on Linux +# No additional configuration needed +``` + +**macOS: Optimize Volume Mounts** +```bash +# Use :delegated flag (already in Makefile) +-v $(PWD):/var/www/html:delegated + +# Or use Docker volumes for vendor/ +# docker volume create --name vendor-cache +-v vendor-cache:/var/www/html/vendor +``` + +**Windows: Enable WSL2** +```powershell +# Use WSL2 backend (faster than Hyper-V) +wsl --set-default-version 2 +``` + +### Issue 5: Image Pull Fails + +**Symptoms:** +```bash +$ make docker-pull +Error response from daemon: manifest not found +``` + +**Solutions:** +```bash +# Check image exists +docker search kariricode/php-api-stack + +# Try explicit tag +docker pull kariricode/php-api-stack:dev + +# Check Docker Hub status +curl -s https://status.docker.com/api/v2/status.json +``` + +--- + +## Best Practices + +### 1. Pre-commit Workflow +```bash +# Fast local checks +make pre-commit + +# Final validation in Docker (before push) +make docker-ci + +# Catches environment-specific issues +``` + +### 2. CI/CD Strategy +```bash +# GitHub Actions / GitLab CI +# Always use Docker for consistency + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run CI in Docker + run: make docker-ci +``` + +### 3. Team Onboarding +```bash +# New developer setup (no local PHP needed) +git clone https://github.com/org/repo.git +cd repo + +# Pull image +make docker-pull + +# Install dependencies in container +make docker-composer CMD="install" + +# Run tests +make docker-test + +# ✅ Ready to develop! +``` + +### 4. Debugging in Docker +```bash +# Interactive debugging session +make docker-shell + +# Inside container: +root@abc:/var/www/html# php -d xdebug.mode=debug \ + -d xdebug.start_with_request=yes \ + vendor/bin/phpunit --filter testMethod + +# Configure IDE to listen on host:9003 +``` + +### 5. Cache Optimization +```bash +# Use Docker volumes for vendor/ +# Speeds up repeated container starts + +# Create volume +docker volume create composer-cache + +# Use in Makefile +DOCKER_RUN := docker run --rm \ + -v $(PWD):/var/www/html \ + -v composer-cache:/var/www/html/vendor \ + kariricode/php-api-stack:dev +``` + +### 6. Resource Limits +```bash +# Prevent runaway containers + +# Limit memory +DOCKER_RUN := docker run --rm \ + -v $(PWD):/var/www/html \ + --memory="1g" \ + --memory-swap="1g" \ + kariricode/php-api-stack:dev + +# Limit CPU +DOCKER_RUN := docker run --rm \ + -v $(PWD):/var/www/html \ + --cpus="2.0" \ + kariricode/php-api-stack:dev +``` + +--- + +## Command Reference + +### Core Commands +```bash +make docker-shell # Interactive bash shell +make docker-composer CMD="..." # Run Composer command +make docker-php CMD="..." # Run PHP command +``` + +### QA Commands +```bash +make docker-test # Run tests +make docker-test-unit # Unit tests +make docker-phpstan # Static analysis +make docker-psalm # Type checking +make docker-analyse # All analysis +make docker-cs-check # Code style check +make docker-format # Auto-fix style +make docker-lint # Syntax check +make docker-coverage # Code coverage +make docker-bench # Benchmarks +``` + +### CI Pipelines +```bash +make docker-ci # Fast CI pipeline +make docker-ci-full # Full CI pipeline +``` + +### Image Management +```bash +make docker-pull # Pull latest image +make docker-info # Show environment info +make docker-clean # Clean unused resources +``` + +### Utility Tools +```bash +make docker-jq CMD="..." # JSON processing +make docker-yq CMD="..." # YAML processing +make docker-less CMD="..." # View files +make docker-vim CMD="..." # Edit files +make docker-lsof CMD="..." # List open files +make docker-strace CMD="..." # Trace system calls +make docker-ip CMD="..." # Network config +make docker-nc CMD="..." # Network connectivity +``` + +--- + +## Quick Reference Card +``` +╔═══════════════════════════════════════════════════════════╗ +║ Docker Commands Quick Reference ║ +╠═══════════════════════════════════════════════════════════╣ +║ SHELL │ make docker-shell ║ +║ COMPOSER │ make docker-composer CMD="install" ║ +║ PHP │ make docker-php CMD="-v" ║ +║──────────────┼────────────────────────────────────────────║ +║ TEST │ make docker-test ║ +║ ANALYSE │ make docker-analyse ║ +║ FORMAT │ make docker-format ║ +║ COVERAGE │ make docker-coverage ║ +║──────────────┼────────────────────────────────────────────║ +║ FAST CI │ make docker-ci ║ +║ FULL CI │ make docker-ci-full ║ +║──────────────┼────────────────────────────────────────────║ +║ PULL IMAGE │ make docker-pull ║ +║ INFO │ make docker-info ║ +║ CLEAN │ make docker-clean ║ +╚═══════════════════════════════════════════════════════════╝ + +When to Use Docker: + ✅ CI/CD pipelines (always) + ✅ Pre-push validation + ✅ Team consistency + ✅ Testing specific PHP versions + +When to Use Local: + ✅ Daily development + ✅ Fast iteration + ✅ IDE integration +``` + +--- + +**Version**: 1.0.0 +**Modules**: `Makefile.docker-*.mk` (except `docker-compose.mk`) + +**Maintainer**: Walmir Silva +**Note**: For Docker Compose full-stack environment management, see [MAKEFILE-compose.md](MAKEFILE-compose.md) +``` diff --git a/.docs/MAKEFILE-helpers.md b/.docs/MAKEFILE-helpers.md new file mode 100644 index 0000000..8458bb5 --- /dev/null +++ b/.docs/MAKEFILE-helpers.md @@ -0,0 +1,1016 @@ +
+ +# Development Helpers + +[![KaririCode](https://img.shields.io/badge/KaririCode-DevKit-6D00CC?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) +[![Benchmarks](https://img.shields.io/badge/PHPBench-Performance-FF6F00?style=for-the-badge)](https://github.com/phpbench/phpbench) +[![Git](https://img.shields.io/badge/Git-Hooks-F05032?style=for-the-badge&logo=git)](https://git-scm.com) + +**kariricode/devkit** - Professional development environment for KaririCode Framework + +Part of the [KaririCode Framework](https://kariricode.org) ecosystem + +[Main Documentation](MAKEFILE.md) | [GitHub Repository](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Benchmarking](#benchmarking) +3. [Git Hooks Management](#git-hooks-management) +4. [Release Management](#release-management) +5. [Project Information](#project-information) +6. [Statistics & Metrics](#statistics--metrics) +7. [Best Practices](#best-practices) + +--- + +## Overview + +### Scope + +The helpers module (`Makefile.helpers.mk`) provides developer productivity tools: +- **Benchmarking**: Performance measurement with PHPBench +- **Git Hooks**: Automated pre-commit quality checks +- **Release Management**: Version tagging and release preparation +- **Project Info**: Environment and tool verification +- **Statistics**: Code metrics and project analytics + +### Module Architecture +``` +Helpers Module (.make/local/Makefile.helpers.mk) +│ +├── Benchmarking +│ ├── bench → Unified benchmark command +│ └── bench-help → Parameter documentation +│ +├── Git Hooks +│ ├── git-hooks-setup → Install pre-commit hook +│ ├── git-hooks-check → Verify installation +│ └── git-hooks-remove → Cleanup hooks +│ +├── Release Management +│ ├── tag → Create version tag +│ └── release → Release preparation +│ +├── Information +│ └── info → Environment details +│ +└── Statistics + ├── stats → Project statistics + └── loc → Lines of code count +``` + +--- + +## Benchmarking + +### Unified Benchmark Interface + +#### Overview + +The `make bench` command provides a **single, parameter-driven interface** for all benchmarking scenarios: +- Standard benchmarks +- Performance comparisons +- Result storage with tagging +- Automated baseline management + +#### Basic Usage +```bash +# Simple benchmark run +make bench + +# If no 'main' baseline exists: +# → Running benchmarks (unified target)… +# ⚠ No 'main' reference found. Running without comparison. +# Hint: make bench STORE=1 TAG=main ENFORCE_MAIN=1 +# ✓ Benchmarks complete +``` + +### Parameters + +| Parameter | Values | Default | Description | +|-----------|--------|---------|-------------| +| **REF** | `auto`, `main`, `` | `auto` | Compare against stored reference | +| **STORE** | `0`, `1` | `0` | Store results with tag | +| **TAG** | `` | - | Tag name when storing (required if STORE=1) | +| **ENFORCE_MAIN** | `0`, `1` | `0` | Require 'main' branch when TAG=main | +| **REPORT** | `0`, `1` | `0` | Save output to `build/benchmarks/last.txt` | + +### Reference Comparison (REF) + +#### Auto-detect Reference +```bash +# Try 'main' baseline if available +make bench REF=auto + +# If 'main' exists: +# ✓ 'main' reference found. Enabling comparison… +# Shows comparison against main baseline + +# If 'main' doesn't exist: +# ⚠ No 'main' reference found. Running without comparison. +``` + +#### Compare Against Main +```bash +make bench REF=main + +# Output: +# → Comparing against reference: main +# +# Subject Groups Iters Mean Diff (%) +# benchTokenize 10 127.234μs -12.5% ← Faster +# benchParse 10 456.789μs +5.2% ← Slower +``` + +#### Compare Against Custom Tag +```bash +make bench REF=feature-x + +# Compares current code against stored 'feature-x' baseline +``` + +### Storing Results (STORE + TAG) + +#### Store Feature Baseline +```bash +make bench STORE=1 TAG=feature-optimization + +# → Storing run with tag 'feature-optimization'… +# Stores current performance metrics +# ✓ Benchmarks complete +``` + +#### Store Main Baseline (with branch enforcement) +```bash +# Must be on 'main' branch +git checkout main +make bench STORE=1 TAG=main ENFORCE_MAIN=1 + +# If not on main: +# ✗ This action requires branch 'main' (current: develop) + +# If on main: +# → Storing run with tag 'main'… +# ✓ Benchmarks complete +``` + +### Save Output to File (REPORT) +```bash +# Compare and save detailed report +make bench REF=main REPORT=1 + +# Output saved to: build/benchmarks/last.txt +# ✓ Output saved to build/benchmarks/last.txt + +# View report +cat build/benchmarks/last.txt +``` + +### Complete Workflow Examples + +#### Baseline Creation Workflow +```bash +# 1. Create initial baseline on main branch +git checkout main +git pull origin main + +# 2. Store as 'main' reference (with enforcement) +make bench STORE=1 TAG=main ENFORCE_MAIN=1 + +# Output: +# → Storing run with tag 'main'… +# +# Subject Groups Iters Mean +# benchTokenize 10 145.234μs +# benchParse 10 432.156μs +# +# ✓ Benchmarks complete +``` + +#### Optimization Workflow +```bash +# 1. Create optimization branch +git checkout -b optimize/lexer-performance + +# 2. Make optimization changes +# (edit source files) + +# 3. Run benchmark with comparison +make bench REF=main + +# Output shows performance delta: +# benchTokenize: 127.234μs (-12.4%) ← 12.4% faster! ✓ + +# 4. Store optimization attempt +make bench STORE=1 TAG=opt-attempt-1 + +# 5. Make more changes +# (edit source files) + +# 6. Compare against previous attempt +make bench REF=opt-attempt-1 + +# 7. Save final report +make bench REF=main REPORT=1 + +# 8. Review detailed comparison +cat build/benchmarks/last.txt +``` + +#### Feature Development Workflow +```bash +# 1. Create feature branch +git checkout -b feature/new-parser + +# 2. Develop feature with periodic benchmarks +make bench REF=main + +# 3. Store milestone benchmarks +make bench STORE=1 TAG=feature-milestone-1 +# (develop more) +make bench STORE=1 TAG=feature-milestone-2 + +# 4. Compare milestones +make bench REF=feature-milestone-1 + +# 5. Before merging to main +make bench REF=main REPORT=1 +# Ensure no performance regression +``` + +#### Release Workflow +```bash +# Before release, verify performance +git checkout main +make bench REF=main REPORT=1 + +# Store release baseline +make bench STORE=1 TAG=v2.0.0 + +# Archive report +cp build/benchmarks/last.txt docs/performance/v2.0.0-benchmark.txt +git add docs/performance/v2.0.0-benchmark.txt +git commit -m "docs: add v2.0.0 performance baseline" +``` + +### Benchmark Help +```bash +make bench-help + +# Shows comprehensive parameter documentation: +# +# ╔════════════════════════════════════════════════════════╗ +# ║ Benchmark Command Help ║ +# ╚════════════════════════════════════════════════════════╝ +# +# Single entrypoint: make bench +# +# Parameters: +# REF=auto|main| Compare against a stored tag +# STORE=1 TAG= Store benchmarks with a tag +# ENFORCE_MAIN=1 Require 'main' branch for TAG=main +# REPORT=1 Save output to last.txt +# +# Examples: +# make bench # Normal run +# make bench REF=main # Compare vs main +# make bench STORE=1 TAG=feat # Store as 'feat' +# make bench REF=main REPORT=1 # Compare and save +``` + +### Benchmark Configuration + +#### PHPBench Configuration (phpbench.json) +```json +{ + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "benchmarks", + "runner.progress": "dots", + "runner.iterations": [10], + "runner.revs": [100], + "runner.warmup": [1], + "report.generators": { + "default": { + "extends": "aggregate" + } + } +} +``` + +#### Benchmark Example +```php +// benchmarks/LexerBench.php +lexer = new Lexer(); + $this->sourceCode = file_get_contents(__DIR__ . '/fixtures/sample.php'); + } + + /** + * @Subject + */ + public function benchTokenize(): void + { + $this->lexer->tokenize($this->sourceCode); + } + + /** + * @Subject + * @ParamProviders({"provideCodeSamples"}) + */ + public function benchTokenizeVariousSizes(array $params): void + { + $this->lexer->tokenize($params['code']); + } + + public function provideCodeSamples(): \Generator + { + yield 'small' => ['code' => ' ['code' => file_get_contents(__DIR__ . '/fixtures/medium.php')]; + yield 'large' => ['code' => file_get_contents(__DIR__ . '/fixtures/large.php')]; + } +} +``` + +### Performance Analysis Tips + +#### Interpret Results +``` +Subject Groups Iters Mean Stddev RPS +benchTokenize lexer 10 127.234μs ±2.34% 7,859.2 + +Mean: Average execution time +Stddev: Standard deviation (consistency indicator) +RPS: Requests per second (throughput) +``` + +**Good Performance:** +- Low mean time +- Low standard deviation (< 5%) +- High RPS + +#### Optimization Checklist +```bash +# 1. Create baseline +make bench STORE=1 TAG=baseline + +# 2. Profile hot paths +make bench REF=baseline +# Identify slowest operations + +# 3. Optimize critical sections +# (implement optimizations) + +# 4. Verify improvement +make bench REF=baseline +# Must show negative % (faster) + +# 5. Check for regressions +make bench REF=main +# Ensure no other operations slowed down +``` + +--- + +## Git Hooks Management + +### Pre-commit Hook Installation + +#### Setup Hook +```bash +make git-hooks-setup + +# What it does: +# 1. Creates .git/hooks/pre-commit +# 2. Backs up existing hook (if present) → .git/hooks/pre-commit.bak +# 3. Writes 'make pre-commit' script +# 4. Makes hook executable (chmod +x) + +# Output: +# → Setting up git hooks... +# ✓ Git hooks set up +``` + +#### Hook Script Content +```bash +# .git/hooks/pre-commit +#!/bin/sh +set -e +make pre-commit +``` + +**Behavior:** +- Runs automatically before each commit +- Executes: `format → lint → analyse → test-unit` +- Blocks commit if any check fails +- Exit code determines commit success + +### Verify Installation +```bash +make git-hooks-check + +# Checks: +# 1. Hook file exists (.git/hooks/pre-commit) +# 2. Contains 'make pre-commit' command +# 3. Is executable + +# Output (if installed): +# → Verifying git hooks... +# ✓ pre-commit hook is installed correctly + +# Output (if not installed): +# → Verifying git hooks... +# ✗ pre-commit hook not found +``` + +### Remove Hook +```bash +make git-hooks-remove + +# What it does: +# 1. Checks for backup (.git/hooks/pre-commit.bak) +# 2. If backup exists: Restores it +# 3. If no backup: Removes generated hook +# 4. Cleanup + +# Output (with backup): +# → Cleaning up git hooks... +# ↩ Restoring backup pre-commit hook... +# ✓ Git hooks cleaned + +# Output (without backup): +# → Cleaning up git hooks... +# ✗ Removing generated pre-commit hook... +# ✓ Git hooks cleaned +``` + +### Hook Workflow + +#### Commit with Hook Active +```bash +$ git add src/Parser.php +$ git commit -m "feat: optimize parser performance" + +# Hook executes automatically: +→ Running pre-commit checks... + +→ Formatting code... +✓ Code formatted + +→ Linting PHP files... +✓ All PHP files are valid + +→ Running static analysis... +✓ PHPStan analysis passed +✓ Psalm analysis passed + +→ Running unit tests... +OK (95 tests, 280 assertions) +✓ Unit tests passed + +✓ Pre-commit checks passed + +[feature/optimize abc123] feat: optimize parser performance + 1 file changed, 10 insertions(+), 5 deletions(-) +``` + +#### Commit Failed by Hook +```bash +$ git commit -m "wip: broken code" + +→ Running pre-commit checks... + +→ Formatting code... +✓ Code formatted + +→ Linting PHP files... +Parse error: syntax error, unexpected 'class' +✗ Linting failed + +# Commit blocked - fix issues first +``` + +#### Bypass Hook (Emergency) +```bash +# Override hook (use sparingly!) +git commit --no-verify -m "emergency: critical hotfix" + +# ⚠️ Still run checks after: +make pre-commit +``` + +### Custom Hook Configuration + +#### Conditional Execution +```bash +# Edit: .git/hooks/pre-commit +#!/bin/sh +set -e + +# Get staged files +STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$' || true) + +# Only run checks if PHP files changed +if [ -n "$STAGED" ]; then + echo "PHP files changed, running checks..." + make pre-commit +else + echo "No PHP files changed, skipping checks" +fi +``` + +#### Partial Checks +```bash +# Edit: .git/hooks/pre-commit +#!/bin/sh +set -e + +# Check if only documentation changed +CHANGED=$(git diff --cached --name-only) + +if echo "$CHANGED" | grep -qv "\.md$"; then + # Source code changed: full checks + make pre-commit +else + # Only docs changed: format and lint only + make format + make lint +fi +``` + +--- + +## Release Management + +### Create Version Tag + +#### Basic Usage +```bash +make tag VERSION=1.0.0 + +# Executes: +# 1. Validates VERSION parameter +# 2. Creates annotated git tag: v1.0.0 +# 3. Pushes tag to origin + +# Output: +# → Creating tag v1.0.0... +# ✓ Tag v1.0.0 created and pushed +``` + +#### Tag Format +```bash +# Semantic Versioning (MAJOR.MINOR.PATCH) +make tag VERSION=1.0.0 # v1.0.0 +make tag VERSION=2.1.0 # v2.1.0 +make tag VERSION=2.1.5 # v2.1.5 + +# Pre-release tags +make tag VERSION=1.0.0-alpha.1 +make tag VERSION=2.0.0-beta.2 +make tag VERSION=1.0.0-rc.1 +``` + +#### Tag Annotation +```makefile +# Makefile creates annotated tag with message +git tag -a "v$(VERSION)" -m "Release v$(VERSION)" + +# View tag details: +git show v1.0.0 +``` + +### Release Preparation +```bash +make release + +# Executes complete release workflow: +# 1. make cd → Full validation pipeline +# 2. Display checklist → Manual steps reminder + +# Output: +# ╔════════════════════════════════════════════════════════╗ +# ║ KaririCode\DevKit CD Pipeline ║ +# ╚════════════════════════════════════════════════════════╝ +# +# [Runs ci-full + benchmarks...] +# +# ✓ Release preparation complete +# +# Next steps: +# 1. Update CHANGELOG.md +# 2. Update version in composer.json +# 3. Commit changes +# 4. Run: make tag VERSION=X.Y.Z +# 5. Push to GitHub +# 6. Create GitHub release +``` + +### Complete Release Workflow + +#### Step-by-Step Release +```bash +# 1. Ensure clean state +git checkout main +git pull origin main +git status # Should be clean + +# 2. Run release preparation +make release + +# 3. Update CHANGELOG.md +nano CHANGELOG.md +# Add release notes: +# ## [2.0.0] - 2025-01-15 +# ### Added +# - New feature X +# ### Changed +# - Improved Y +# ### Fixed +# - Bug Z + +# 4. Update version in composer.json +nano composer.json +# Change: "version": "2.0.0" + +# 5. Commit changes +git add CHANGELOG.md composer.json +git commit -m "chore: release v2.0.0" + +# 6. Create and push tag +make tag VERSION=2.0.0 + +# 7. Push commits +git push origin main + +# 8. Create GitHub release (manual or gh CLI) +gh release create v2.0.0 \ + --title "Release v2.0.0" \ + --notes-file CHANGELOG.md \ + --generate-notes +``` + +#### Automated Release Script +```bash +#!/bin/bash +# scripts/release.sh + +set -e + +VERSION=$1 + +if [ -z "$VERSION" ]; then + echo "Usage: ./scripts/release.sh VERSION" + exit 1 +fi + +echo "Preparing release $VERSION..." + +# 1. Run release checks +make release + +# 2. Update version in composer.json +sed -i "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" composer.json + +# 3. Update CHANGELOG +DATE=$(date +%Y-%m-%d) +sed -i "s/## \[Unreleased\]/## [$VERSION] - $DATE/" CHANGELOG.md + +# 4. Commit +git add composer.json CHANGELOG.md +git commit -m "chore: release v$VERSION" + +# 5. Tag +make tag VERSION=$VERSION + +# 6. Push +git push origin main + +echo "✓ Release $VERSION complete!" +``` + +--- + +## Project Information + +### Environment Details +```bash +make info + +# Shows comprehensive project information: +# +# ╔════════════════════════════════════════════════════════╗ +# ║ Project Information ║ +# ╚════════════════════════════════════════════════════════╝ +# +# PHP Version: 8.4.14 +# PHP Binary: /usr/bin/php +# Composer: /usr/local/bin/composer +# Project Directory: /home/user/kariricode-devkit +# Source Directory: src +# Test Directory: tests +# +# ╔════════════════════════════════════════════════════════╗ +# ║ Installed Tools ║ +# ╚════════════════════════════════════════════════════════╝ +# +# PHPUnit: ✓ +# PHPStan: ✓ +# Psalm: ✓ +# PHPCS: ✓ +# PHP-CS-Fixer: ✓ +# Infection: ✓ +# PHPBench: ✓ +``` + +### Use Cases + +**New Developer Onboarding:** +```bash +# Clone repository +git clone https://github.com/KaririCode-Framework/kariricode-devkit.git +cd kariricode-devkit + +# Verify environment +make info + +# If tools missing (✗): +make install-dev +make info # Should all show ✓ +``` + +**Troubleshooting:** +```bash +# Check environment before reporting issues +make info > environment.txt + +# Include in bug report +``` + +**Documentation:** +```bash +# Generate environment report for README +make info +# Copy output to documentation +``` + +--- + +## Statistics & Metrics + +### Project Statistics +```bash +make stats + +# Comprehensive project metrics: +# +# ╔════════════════════════════════════════════════════════╗ +# ║ Project Statistics ║ +# ╚════════════════════════════════════════════════════════╝ +# +# Total PHP files: 150 +# Total test files: 95 +# Lines of code: 12,456 +# Lines of tests: 8,932 +# +# ╔════════════════════════════════════════════════════════╗ +# ║ Directory Sizes ║ +# ╚════════════════════════════════════════════════════════╝ +# +# src 2.3M +# tests 1.8M +# vendor 45M +``` + +### Lines of Code +```bash +make loc + +# Quick line count: +# +# → Counting lines of code... +# Source: 12456 lines +# Tests: 8932 lines +``` + +### Metrics Over Time +```bash +# Track code growth weekly +{ + echo "$(date): $(make loc 2>&1 | grep Source)" +} >> metrics/weekly-loc.txt + +# Visualize growth +cat metrics/weekly-loc.txt +# 2025-01-08: Source: 11234 lines +# 2025-01-15: Source: 12456 lines +# Growth: +1222 lines (+10.9%) +``` + +### Test Coverage Ratio +```bash +# Calculate test-to-code ratio +SRC_LINES=$(find src -name '*.php' -exec wc -l {} \; | awk '{sum += $1} END {print sum}') +TEST_LINES=$(find tests -name '*.php' -exec wc -l {} \; | awk '{sum += $1} END {print sum}') + +RATIO=$(echo "scale=2; $TEST_LINES / $SRC_LINES" | bc) +echo "Test-to-Code Ratio: $RATIO" + +# Good ratios: +# 0.5-0.8: Healthy (50-80% test code relative to source) +# 0.8-1.2: Excellent (comprehensive testing) +# > 1.2: Possibly over-testing +``` + +--- + +## Best Practices + +### 1. Benchmark Regularly +```bash +# Weekly performance checks +make bench REF=main REPORT=1 + +# Before optimization +make bench STORE=1 TAG=pre-optimization + +# After optimization +make bench REF=pre-optimization REPORT=1 +``` + +### 2. Enforce Pre-commit Hooks +```bash +# Team setup script +# setup-team.sh +#!/bin/bash + +echo "Setting up development environment..." +make install-dev +make git-hooks-setup +make info + +echo "✓ Setup complete. Pre-commit hooks active." +``` + +### 3. Document Releases Thoroughly +```markdown + +## [2.0.0] - 2025-01-15 + +### Added +- New parser architecture with AST nodes +- Benchmark suite for performance tracking + +### Changed +- **BREAKING**: Renamed `Lexer::parse()` to `Lexer::tokenize()` +- Improved error messages with context + +### Fixed +- Memory leak in recursive parsing +- Thread safety in token cache + +### Performance +- 25% faster tokenization (see benchmarks/v2.0.0.txt) +- 40% reduction in memory usage + +### Security +- Fixed XSS vulnerability in error output (CVE-2025-XXXX) +``` + +### 4. Automate Release Process +```bash +# .github/workflows/release.yml +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to release (e.g., 1.0.0)' + required: true + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + + - name: Run release checks + run: make release + + - name: Update version + run: | + sed -i "s/\"version\": \".*\"/\"version\": \"${{ github.event.inputs.version }}\"/" composer.json + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git add composer.json + git commit -m "chore: release v${{ github.event.inputs.version }}" + + - name: Create tag + run: make tag VERSION=${{ github.event.inputs.version }} +``` + +--- + +## Command Reference + +### Benchmarking +```bash +make bench # Run benchmarks +make bench REF=main # Compare against main +make bench STORE=1 TAG=feature # Store with tag +make bench REF=main REPORT=1 # Compare and save report +make bench-help # Show parameter help +``` + +### Git Hooks +```bash +make git-hooks-setup # Install pre-commit hook +make git-hooks-check # Verify installation +make git-hooks-remove # Remove hook +``` + +### Release Management +```bash +make tag VERSION=X.Y.Z # Create version tag +make release # Release preparation +``` + +### Information & Stats +```bash +make info # Environment information +make stats # Project statistics +make loc # Lines of code count +``` + +--- + +## Quick Reference Card +``` +╔═══════════════════════════════════════════════════════════╗ +║ Development Helpers Quick Reference ║ +╠═══════════════════════════════════════════════════════════╣ +║ BENCHMARK │ make bench ║ +║ COMPARE │ make bench REF=main ║ +║ STORE │ make bench STORE=1 TAG=name ║ +║ REPORT │ make bench REF=main REPORT=1 ║ +║──────────────┼────────────────────────────────────────────║ +║ SETUP HOOKS │ make git-hooks-setup ║ +║ CHECK HOOKS │ make git-hooks-check ║ +║ REMOVE HOOKS │ make git-hooks-remove ║ +║──────────────┼────────────────────────────────────────────║ +║ CREATE TAG │ make tag VERSION=1.0.0 ║ +║ RELEASE │ make release ║ +║──────────────┼────────────────────────────────────────────║ +║ INFO │ make info ║ +║ STATS │ make stats ║ +║ LOC │ make loc ║ +╚═══════════════════════════════════════════════════════════╝ + +Benchmark Workflow: + 1. make bench STORE=1 TAG=main ENFORCE_MAIN=1 + 2. (make changes) + 3. make bench REF=main + 4. make bench STORE=1 TAG=feature-name + +Release Workflow: + 1. make release + 2. Update CHANGELOG.md + composer.json + 3. git commit -m "chore: release vX.Y.Z" + 4. make tag VERSION=X.Y.Z +``` + +--- + +**Version**: 1.0.0 +**Module**: `Makefile.helpers.mk` +**Maintainer**: Walmir Silva diff --git a/.docs/MAKEFILE-pipeline.md b/.docs/MAKEFILE-pipeline.md new file mode 100644 index 0000000..e44f500 --- /dev/null +++ b/.docs/MAKEFILE-pipeline.md @@ -0,0 +1,949 @@ +
+ +# CI/CD Pipeline Orchestration + +[![KaririCode](https://img.shields.io/badge/KaririCode-DevKit-6D00CC?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) +[![CI](https://img.shields.io/badge/CI-Automated-2088FF?style=for-the-badge&logo=github-actions)](https://github.com/features/actions) +[![CD](https://img.shields.io/badge/CD-Release%20Ready-00C853?style=for-the-badge&logo=gitbook)](https://github.com/features/actions) + +**kariricode/devkit** - Professional development environment for KaririCode Framework + +Part of the [KaririCode Framework](https://kariricode.org) ecosystem + +[Main Documentation](MAKEFILE.md) | [GitHub Repository](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Pipeline Architecture](#pipeline-architecture) +3. [CI Pipelines](#ci-pipelines) +4. [CD Pipeline](#cd-pipeline) +5. [Pre-commit Hooks](#pre-commit-hooks) +6. [CI/CD Integration](#cicd-integration) +7. [Pipeline Optimization](#pipeline-optimization) +8. [Best Practices](#best-practices) + +--- + +## Overview + +### Scope + +The pipeline module (`Makefile.orchestration.mk`) provides: +- **CI Pipeline**: Fast continuous integration checks +- **Full CI Pipeline**: Comprehensive quality validation +- **CD Pipeline**: Release preparation workflow +- **Pre-commit Pipeline**: Developer quality gates + +### Design Philosophy +``` +┌─────────────────────────────────────────────────────────┐ +│ Pipeline Orchestration Philosophy │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ COMPOSABILITY → Build complex from simple │ +│ FAIL-FAST → Stop on first error │ +│ FEEDBACK SPEED → Critical checks first │ +│ IDEMPOTENCY → Same result every run │ +│ ISOLATION → No side effects between stages │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +### Pipeline Composition +```makefile +# Simple targets (atomic operations) +lint: @vendor/bin/phplint +cs-check: @vendor/bin/phpcs +phpstan: @vendor/bin/phpstan +test: @vendor/bin/phpunit + +# Composed pipelines (orchestrated workflows) +ci: lint + cs-check + phpstan + psalm + test +ci-full: ci + coverage + mutation + security +cd: ci-full + bench + release-prep +``` + +--- + +## Pipeline Architecture + +### Execution Flow +``` +┌─────────────────────────────────────────────────────────┐ +│ Pipeline Stages │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ Stage 1: Prerequisites │ +│ ├─ check-php (PHP version validation) │ +│ └─ verify-install (Dependencies check) │ +│ ↓ │ +│ Stage 2: Fast Checks (< 30 seconds) │ +│ ├─ lint (Syntax errors) │ +│ ├─ cs-check (Code style) │ +│ └─ validate (composer.json) │ +│ ↓ │ +│ Stage 3: Static Analysis (1-2 minutes) │ +│ ├─ phpstan (Type errors) │ +│ └─ psalm (Logic errors) │ +│ ↓ │ +│ Stage 4: Testing (1-3 minutes) │ +│ └─ test (Unit + Integration) │ +│ ↓ │ +│ Stage 5: Quality Metrics (3-5 minutes) [OPTIONAL] │ +│ ├─ coverage (Code coverage) │ +│ └─ mutation (Mutation testing) │ +│ ↓ │ +│ Stage 6: Security (< 30 seconds) │ +│ └─ security (Vulnerability scan) │ +│ ↓ │ +│ Stage 7: Performance (2-5 minutes) [CD ONLY] │ +│ └─ bench (Benchmarking) │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +### Fail-Fast Strategy +```makefile +# Each stage must pass before proceeding +ci: + @$(MAKE) --no-print-directory check-php # FAIL → STOP + @$(MAKE) --no-print-directory lint # FAIL → STOP + @$(MAKE) --no-print-directory cs-check # FAIL → STOP + @$(MAKE) --no-print-directory phpstan # FAIL → STOP + @$(MAKE) --no-print-directory psalm # FAIL → STOP + @$(MAKE) --no-print-directory test # FAIL → STOP + @echo "✓ CI pipeline completed" # ALL PASSED +``` + +--- + +## CI Pipelines + +### Fast CI Pipeline + +#### Purpose + +**Optimize for developer feedback speed:** +- ✅ Essential quality checks only +- ✅ Target execution: < 2 minutes +- ✅ Run on every commit +- ✅ High signal-to-noise ratio + +#### Command +```bash +make ci + +# Executes in sequence: +# 1. check-php → PHP 8.4+ validation +# 2. lint → Syntax errors +# 3. cs-check → Code style violations +# 4. phpstan → Static analysis (level max) +# 5. psalm → Type checking +# 6. test → Test suite execution + +# Total time: ~1-2 minutes +``` + +#### Output Example +```bash +$ make ci + +╔════════════════════════════════════════════════════════╗ +║ KaririCode\DevKit CI Pipeline ║ +╚════════════════════════════════════════════════════════╝ + +→ Checking PHP version... +✓ PHP version 8.4.14 OK (>= 8.4.0) + +→ Linting PHP files... +✓ All PHP files are valid + +→ Checking coding standards... +✓ Coding standards check passed + +→ Running PHPStan... +✓ PHPStan analysis passed + +→ Running Psalm... +✓ Psalm analysis passed + +→ Running tests... +OK (150 tests, 420 assertions) +✓ Tests passed + +✓ CI pipeline completed successfully + +Time: 1m 23s +``` + +#### When to Use + +| Scenario | Use CI | +|----------|--------| +| Local development | ✅ Before every commit | +| Pull request checks | ✅ Required status check | +| Branch protection | ✅ Merge requirement | +| Pre-push hook | ✅ Optional automation | + +### Full CI Pipeline + +#### Purpose + +**Comprehensive quality validation:** +- ✅ All CI checks + extended quality metrics +- ✅ Target execution: 3-5 minutes +- ✅ Run before release +- ✅ Production readiness verification + +#### Command +```bash +make ci-full + +# Executes ALL stages: +# 1. check-php → PHP version +# 2. validate → composer.json validation +# 3. security → Vulnerability scan +# 4. lint → Syntax check +# 5. cs-check → Code style +# 6. phpstan → Static analysis +# 7. psalm → Type checking +# 8. test → All tests +# 9. coverage → Code coverage (requires Xdebug) +# 10. mutation → Mutation testing (slow) + +# Total time: ~3-5 minutes +``` + +#### Output Example +```bash +$ make ci-full + +╔════════════════════════════════════════════════════════╗ +║ KaririCode\DevKit Full CI Pipeline ║ +╚════════════════════════════════════════════════════════╝ + +→ Checking PHP version... +✓ PHP version 8.4.14 OK (>= 8.4.0) + +→ Validating composer.json... +✓ composer.json is valid + +→ Checking for security vulnerabilities... +✓ No security vulnerabilities found + +→ Linting PHP files... +✓ All PHP files are valid + +→ Checking coding standards... +✓ Coding standards check passed + +→ Running PHPStan... +✓ PHPStan analysis passed + +→ Running Psalm... +✓ Psalm analysis passed + +→ Running tests... +OK (150 tests, 420 assertions) +✓ Tests passed + +→ Generating code coverage report... +Code Coverage: 79.24% (1234/1557 lines) +✓ Coverage report generated: coverage/html/index.html + +→ Running mutation tests... +Mutation Score Indicator (MSI): 82% +Covered Code MSI: 92% +✓ Mutation testing complete + +✓ Full CI pipeline completed successfully + +Time: 4m 37s +``` + +#### When to Use + +| Scenario | Use CI-Full | +|----------|-------------| +| Release candidates | ✅ Required | +| Main branch merges | ✅ Recommended | +| Weekly quality check | ✅ Best practice | +| Production deployment | ✅ Mandatory | + +### Pipeline Comparison + +| Feature | CI (Fast) | CI-Full | +|---------|-----------|---------| +| **Execution Time** | ~1-2 min | ~3-5 min | +| **PHP Version Check** | ✅ | ✅ | +| **Syntax Linting** | ✅ | ✅ | +| **Code Style** | ✅ | ✅ | +| **Static Analysis** | ✅ | ✅ | +| **Tests** | ✅ | ✅ | +| **Composer Validation** | ❌ | ✅ | +| **Security Scan** | ❌ | ✅ | +| **Code Coverage** | ❌ | ✅ | +| **Mutation Testing** | ❌ | ✅ | +| **Use Case** | Every commit | Pre-release | + +--- + +## CD Pipeline + +### Purpose + +**Complete release readiness validation:** +- ✅ Full CI-Full pipeline +- ✅ Performance benchmarking +- ✅ Release preparation checklist +- ✅ Final production verification + +### Command +```bash +make cd + +# Executes: +# 1. ci-full → Complete quality validation +# 2. bench → Performance baseline +# 3. Release checklist display + +# Total time: ~5-8 minutes +``` + +### Output Example +```bash +$ make cd + +╔════════════════════════════════════════════════════════╗ +║ KaririCode\DevKit CD Pipeline ║ +╚════════════════════════════════════════════════════════╝ + +[Running ci-full pipeline...] +✓ Full CI pipeline completed successfully + +→ Running benchmarks... +✓ Benchmarks complete + +✓ CD pipeline completed - Ready for release + +Next steps: + 1. Update CHANGELOG.md + 2. Update version in composer.json + 3. Commit changes + 4. Run: make tag VERSION=X.Y.Z + 5. Push to GitHub + 6. Create GitHub release + +Time: 6m 12s +``` + +### Release Workflow Integration +```bash +# Complete release workflow + +# 1. Ensure clean state +git checkout main +git pull origin main + +# 2. Run CD pipeline +make cd + +# 3. Update version files +nano CHANGELOG.md +nano composer.json + +# 4. Commit changes +git add CHANGELOG.md composer.json +git commit -m "chore: release v2.0.0" + +# 5. Create and push tag +make tag VERSION=2.0.0 + +# 6. Push changes +git push origin main --tags + +# 7. Create GitHub release (manual or automated) +``` + +--- + +## Pre-commit Hooks + +### Purpose + +**Developer quality gate before commit:** +- ✅ Fast feedback (30-60 seconds) +- ✅ Auto-fix common issues +- ✅ Essential checks only +- ✅ Prevent broken commits + +### Command +```bash +make pre-commit + +# Executes: +# 1. format → Auto-fix code style +# 2. lint → Syntax validation +# 3. analyse → Static analysis (phpstan + psalm) +# 4. test-unit → Unit tests only (fast) + +# Total time: ~30-60 seconds +``` + +### Output Example +```bash +$ make pre-commit + +→ Running pre-commit checks... + +→ Formatting code... +Fixed 3 files in 1.234 seconds +✓ Code formatted + +→ Linting PHP files... +✓ All PHP files are valid + +→ Running static analysis... +✓ PHPStan analysis passed +✓ Psalm analysis passed + +→ Running unit tests... +OK (95 tests, 280 assertions) +✓ Unit tests passed + +✓ Pre-commit checks passed + +Time: 0m 42s +``` + +### Git Hook Installation + +#### Setup Hook +```bash +# Install pre-commit hook +make git-hooks-setup + +# What it does: +# 1. Creates .git/hooks/pre-commit +# 2. Backs up existing hook (if any) +# 3. Makes hook executable +# 4. Configures to run 'make pre-commit' + +# Output: +# → Setting up git hooks... +# ✓ Git hooks set up +``` + +#### Verify Installation +```bash +make git-hooks-check + +# Output: +# → Verifying git hooks... +# ✓ pre-commit hook is installed correctly +``` + +#### Remove Hook +```bash +make git-hooks-remove + +# What it does: +# 1. Removes .git/hooks/pre-commit +# 2. Restores backup (if exists) + +# Output: +# → Cleaning up git hooks... +# ↩ Restoring backup pre-commit hook... +# ✓ Git hooks cleaned +``` + +### Hook Bypass (Emergency) +```bash +# Bypass hook for emergency commits +git commit --no-verify -m "emergency: fix critical bug" + +# ⚠️ Use sparingly - still run checks after: +make pre-commit +``` + +### Custom Pre-commit Configuration +```bash +# Edit .git/hooks/pre-commit after installation +nano .git/hooks/pre-commit + +# Example: Skip tests if changing docs only +#!/bin/sh +set -e + +# Get changed files +CHANGED=$(git diff --cached --name-only) + +# Skip tests if only docs changed +if echo "$CHANGED" | grep -qv "\.md$"; then + make pre-commit +else + echo "Only docs changed, skipping full checks" + make format + make lint +fi +``` + +--- + +## CI/CD Integration + +### GitHub Actions + +#### Basic CI Workflow +```yaml +# .github/workflows/ci.yml +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + extensions: mbstring, xml, pcov + coverage: pcov + + - name: Cache Composer dependencies + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: make install + + - name: Run CI pipeline + run: make ci + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: build/reports/ +``` + +#### Full CI Workflow (with coverage) +```yaml +# .github/workflows/ci-full.yml +name: Full CI + +on: + push: + branches: [ main ] + schedule: + - cron: '0 0 * * 0' # Weekly on Sunday + +jobs: + ci-full: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: xdebug + + - name: Install dependencies + run: make install + + - name: Run Full CI + run: make ci-full + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage/clover.xml + fail_ci_if_error: true + + - name: Upload mutation report + if: always() + uses: actions/upload-artifact@v3 + with: + name: infection-report + path: infection.html +``` + +#### Release Workflow +```yaml +# .github/workflows/release.yml +name: Release + +on: + push: + tags: + - 'v*.*.*' + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + + - name: Install dependencies + run: make install + + - name: Run CD pipeline + run: make cd + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: | + coverage/clover.xml + infection.html + build/benchmarks/last.txt + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +### GitLab CI +```yaml +# .gitlab-ci.yml +stages: + - validate + - test + - quality + - release + +variables: + COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.composer-cache" + +cache: + paths: + - vendor/ + - .composer-cache/ + +# Fast CI (on every commit) +ci: + stage: test + image: kariricode/php-api-stack:dev + script: + - make install + - make ci + artifacts: + reports: + junit: build/reports/junit.xml + only: + - merge_requests + - branches + +# Full CI (on main branch) +ci-full: + stage: quality + image: kariricode/php-api-stack:dev + script: + - make install + - make ci-full + coverage: '/Lines:\s+(\d+\.\d+)%/' + artifacts: + reports: + coverage_report: + coverage_format: cobertura + path: coverage/clover.xml + paths: + - coverage/ + - infection.html + expire_in: 30 days + only: + - main + +# Release preparation +cd: + stage: release + image: kariricode/php-api-stack:dev + script: + - make install + - make cd + artifacts: + paths: + - build/benchmarks/ + only: + - tags +``` + +### Jenkins Pipeline +```groovy +// Jenkinsfile +pipeline { + agent any + + environment { + COMPOSER_HOME = "${WORKSPACE}/.composer" + } + + stages { + stage('Setup') { + steps { + sh 'make check-php' + sh 'make install' + } + } + + stage('Fast CI') { + when { + not { branch 'main' } + } + steps { + sh 'make ci' + } + } + + stage('Full CI') { + when { branch 'main' } + steps { + sh 'make ci-full' + } + post { + always { + junit 'build/reports/junit.xml' + publishHTML([ + reportDir: 'coverage/html', + reportFiles: 'index.html', + reportName: 'Coverage Report' + ]) + } + } + } + + stage('CD') { + when { tag "v*" } + steps { + sh 'make cd' + } + } + } + + post { + failure { + mail to: 'team@example.com', + subject: "Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}", + body: "Check console output at ${env.BUILD_URL}" + } + } +} +``` + +--- + +## Pipeline Optimization + +### Parallel Execution +```makefile +# Example: Parallel static analysis +analyse-parallel: + @echo "Running static analysis in parallel..." + @$(PHPSTAN) analyse src --level=max & \ + PHPSTAN_PID=$$!; \ + $(PSALM) --show-info=true --stats --no-cache & \ + PSALM_PID=$$!; \ + wait $$PHPSTAN_PID $$PSALM_PID + @echo "✓ Analysis complete" +``` + +### Caching Strategy +```yaml +# GitHub Actions cache example +- name: Cache tools + uses: actions/cache@v3 + with: + path: | + ~/.composer/cache + var/cache/phpstan + .php-cs-fixer.cache + key: ${{ runner.os }}-tools-${{ hashFiles('**/composer.lock') }} +``` + +### Incremental Testing +```bash +# Test only changed files +CHANGED_FILES=$(git diff --name-only main...HEAD | grep '\.php$') +vendor/bin/phpunit $(echo $CHANGED_FILES | sed 's/src/tests/g') +``` + +### Skip Slow Tests in CI +```xml + + + + slow + integration + + +``` +```bash +# Fast CI: Skip slow tests +make test # Excludes @slow and @integration + +# Full CI: Include all tests +vendor/bin/phpunit --group slow,integration +``` + +--- + +## Best Practices + +### 1. Pipeline Selection +```bash +# Development (every commit) +make pre-commit # 30-60s + +# Pull request +make ci # 1-2 min + +# Main branch merge +make ci-full # 3-5 min + +# Release +make cd # 5-8 min +``` + +### 2. Fail-Fast Principle +```bash +# ✅ Good: Stop on first failure +make ci +# Lint fails → STOP (no point running tests) + +# ❌ Bad: Continue despite errors +make lint || true +make analyse || true +make test +# Wastes time, unclear which check failed +``` + +### 3. Feedback Speed Optimization +```bash +# Order checks by speed (fast → slow) +1. lint (5-10s) ← Fast feedback +2. cs-check (10-20s) +3. phpstan (30-60s) +4. psalm (30-60s) +5. test (1-2min) +6. coverage (2-3min) ← Slow, run less often +7. mutation (3-5min) +``` + +### 4. Branch Protection Rules + +**GitHub Branch Protection:** +``` +Settings → Branches → main +├─ Require status checks to pass +│ ├─ CI (required) +│ ├─ Full CI (optional) +│ └─ CD (for tags only) +├─ Require conversation resolution +└─ Require linear history +``` + +### 5. Badge Integration +```markdown +# README.md +![CI](https://github.com/user/repo/workflows/CI/badge.svg) +![Coverage](https://codecov.io/gh/user/repo/branch/main/graph/badge.svg) +![Mutation](https://img.shields.io/endpoint?url=https://badge-api.stryker-mutator.io/github.com/user/repo/main) +``` + +### 6. Quality Metrics Tracking +```bash +# Weekly quality report +{ + echo "Quality Report - $(date)" + echo "================================" + make ci-full 2>&1 | tee quality-report.txt + + echo "" + echo "Coverage:" + grep "Lines:" quality-report.txt + + echo "" + echo "Mutation Score:" + grep "MSI:" quality-report.txt +} | mail -s "Weekly Quality Report" team@example.com +``` + +--- + +## Command Reference + +### Pipeline Commands +```bash +make check # Alias for 'ci' +make ci # Fast CI pipeline (~1-2 min) +make ci-full # Full CI pipeline (~3-5 min) +make cd # CD pipeline (~5-8 min) +make pre-commit # Pre-commit checks (~30-60s) +``` + +### Git Hook Management +```bash +make git-hooks-setup # Install pre-commit hook +make git-hooks-check # Verify hook installation +make git-hooks-remove # Remove and restore backup +``` + +--- + +## Quick Reference Card +``` +╔═══════════════════════════════════════════════════════════╗ +║ CI/CD Pipeline Quick Reference ║ +╠═══════════════════════════════════════════════════════════╣ +║ PRE-COMMIT │ make pre-commit (~30-60s) ║ +║ FAST CI │ make ci (~1-2 min) ║ +║ FULL CI │ make ci-full (~3-5 min) ║ +║ CD RELEASE │ make cd (~5-8 min) ║ +║──────────────┼───────────────────────────────────────────║ +║ GIT HOOKS │ make git-hooks-setup ║ +║ VERIFY HOOKS │ make git-hooks-check ║ +║ REMOVE HOOKS │ make git-hooks-remove ║ +╚═══════════════════════════════════════════════════════════╝ + +Pipeline Selection: + Local development: make pre-commit + Pull request: make ci + Main branch: make ci-full + Release tag: make cd + +Execution Order (fail-fast): + 1. PHP version → 2. Lint → 3. Style → 4. Analysis → 5. Tests +``` + +--- + +**Version**: 1.0.0 +**Module**: `Makefile.orchestration.mk` +**Maintainer**: Walmir Silva diff --git a/.docs/MAKEFILE-qa.md b/.docs/MAKEFILE-qa.md new file mode 100644 index 0000000..c45f884 --- /dev/null +++ b/.docs/MAKEFILE-qa.md @@ -0,0 +1,1279 @@ +
+ +# Quality Assurance + +[![KaririCode](https://img.shields.io/badge/KaririCode-DevKit-6D00CC?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) +[![PHPUnit](https://img.shields.io/badge/PHPUnit-11.x-3776AB?style=for-the-badge&logo=php)](https://phpunit.de) +[![PHPStan](https://img.shields.io/badge/PHPStan-Level%20Max-4F5B93?style=for-the-badge)](https://phpstan.org) +[![Psalm](https://img.shields.io/badge/Psalm-Type%20Safety-8A2BE2?style=for-the-badge)](https://psalm.dev) + +**kariricode/devkit** - Professional development environment for KaririCode Framework + +Part of the [KaririCode Framework](https://kariricode.org) ecosystem + +[Main Documentation](MAKEFILE.md) | [GitHub Repository](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Testing](#testing) +3. [Code Linting](#code-linting) +4. [Static Analysis](#static-analysis) +5. [Code Style & Formatting](#code-style--formatting) +6. [Code Coverage](#code-coverage) +7. [Mutation Testing](#mutation-testing) +8. [Troubleshooting](#troubleshooting) +9. [Best Practices](#best-practices) + +--- + +## Overview + +### Scope + +The QA module (`Makefile.qa.mk`) provides comprehensive quality assurance tools: +- **Testing**: PHPUnit test execution (unit, integration, functional) +- **Linting**: PHP syntax validation +- **Static Analysis**: PHPStan and Psalm +- **Code Style**: PHPCS and PHP-CS-Fixer +- **Coverage**: Code coverage reporting +- **Mutation**: Infection mutation testing + +### Quality Gates +``` +┌─────────────────────────────────────────────────────────┐ +│ Quality Pipeline │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ 1. LINT → Syntax errors? → FAIL │ +│ 2. CS-CHECK → Style violations? → FAIL │ +│ 3. PHPSTAN → Type errors? → FAIL │ +│ 4. PSALM → Logic errors? → FAIL │ +│ 5. TEST → Tests failing? → FAIL │ +│ 6. COVERAGE → Coverage < 80%? → WARN │ +│ 7. MUTATION → MSI < 80%? → WARN │ +│ │ +│ ✓ ALL PASSED → Ready for production │ +└─────────────────────────────────────────────────────────┘ +``` + +### Tools Configuration + +| Tool | Config File | Purpose | +|------|-------------|---------| +| PHPUnit | `phpunit.xml` | Test execution | +| PHPStan | `phpstan.neon` | Static analysis (level max) | +| Psalm | `psalm.xml` | Type checking & taint analysis | +| PHPCS | `phpcs.xml` | PSR-12 style checking | +| PHP-CS-Fixer | `.php-cs-fixer.php` | Modern PHP style fixing | +| Infection | `infection.json` | Mutation testing | +| PHPBench | `phpbench.json` | Performance benchmarking | + +--- + +## Testing + +### Test Execution + +#### Run All Tests +```bash +make test + +# Executes: +# vendor/bin/phpunit --colors=always --testdox + +# Output: +# PHPUnit 11.5.2 by Sebastian Bergmann and contributors. +# +# Parser\Lexer (KaririCode\Parser\Tests\Unit\Lexer) +# ✔ Tokenizes simple string +# ✔ Handles whitespace correctly +# ✔ Recognizes keywords +# +# Time: 00:01.234, Memory: 12.00 MB +# +# OK (150 tests, 420 assertions) +``` + +**Options:** +- `--colors=always`: Colored output +- `--testdox`: Human-readable test names + +#### Test Suites +```bash +# Unit tests only (fast, isolated) +make test-unit + +# Integration tests (with dependencies) +make test-integration + +# Functional tests (end-to-end) +make test-functional +``` + +**Test Suite Configuration (phpunit.xml):** +```xml + + + tests/Unit + + + tests/Integration + + + tests/Functional + + +``` + +### Test Organization + +#### Directory Structure +``` +tests/ +├── Unit/ # Pure unit tests +│ ├── Lexer/ +│ │ ├── LexerTest.php +│ │ └── TokenTest.php +│ ├── Parser/ +│ │ └── ParserTest.php +│ └── ... +│ +├── Integration/ # Component integration +│ ├── LexerParserTest.php +│ └── ... +│ +├── Functional/ # End-to-end scenarios +│ ├── CompleteParsingTest.php +│ └── ... +│ +├── Fixtures/ # Test data +│ ├── valid_code.php +│ └── invalid_code.php +│ +└── bootstrap.php # Test initialization +``` + +#### Test Naming Conventions +```php +// ✅ Good: Descriptive, follows conventions +class LexerTest extends TestCase +{ + public function testTokenizesSimpleString(): void + { + // Arrange + $lexer = new Lexer(); + + // Act + $tokens = $lexer->tokenize('assertCount(5, $tokens); + } +} + +// ❌ Bad: Vague test name +class Test1 extends TestCase +{ + public function test(): void + { + $this->assertTrue(true); + } +} +``` + +### Test Execution Modes + +#### Standard Mode +```bash +# Default: All tests with testdox output +make test +``` + +#### Verbose Mode +```bash +# Detailed output for debugging +vendor/bin/phpunit --testdox --verbose +``` + +#### Stop on Failure +```bash +# Stop at first failure +vendor/bin/phpunit --stop-on-failure +``` + +#### Filter Specific Tests +```bash +# By test method name +vendor/bin/phpunit --filter testTokenizesSimpleString + +# By class name +vendor/bin/phpunit --filter LexerTest + +# By namespace pattern +vendor/bin/phpunit --filter "Parser\\Lexer" +``` + +--- + +## Code Linting + +### Syntax Validation +```bash +make lint + +# Executes: +# find src tests -name "*.php" -print0 | xargs -0 -n1 php -l > /dev/null + +# Checks: +# - PHP syntax errors +# - Parse errors +# - Fatal syntax violations + +# Output: +# → Linting PHP files... +# ✓ All PHP files are valid +``` + +### What Lint Catches + +**Valid Code:** +```php +children; +} + +// ✅ Fixed +public function getChildren(): array +{ + return $this->children; +} +``` + +**Issue: Undefined Property** +```php +// ❌ PHPStan error +class Node +{ + public function __construct() + { + $this->name = 'default'; // Property not declared + } +} + +// ✅ Fixed +class Node +{ + private string $name; + + public function __construct() + { + $this->name = 'default'; + } +} +``` + +**Issue: Possibly Null Reference** +```php +// ❌ PHPStan error +public function process(?Node $node): string +{ + return $node->getName(); // $node might be null +} + +// ✅ Fixed (Option 1: Guard) +public function process(?Node $node): string +{ + if ($node === null) { + throw new \InvalidArgumentException('Node cannot be null'); + } + return $node->getName(); +} + +// ✅ Fixed (Option 2: Null coalescing) +public function process(?Node $node): string +{ + return $node?->getName() ?? 'default'; +} +``` + +### Psalm + +#### Basic Analysis +```bash +make psalm + +# Executes: +# vendor/bin/psalm --show-info=true --stats --no-cache + +# Configuration (psalm.xml): + + + + + +``` + +#### Output Example +```bash +$ make psalm + +→ Running Psalm... + +Scanning files... +Analyzing files... + +████████████████████████████████████████████ 100% + +------------------------------ + ISSUES FOUND +------------------------------ + +ERROR: UnimplementedAbstractMethod - src/Parser/Node/GreenTree/Expression.php:15 + Class KaririCode\Parser\Node\GreenTree\Expression\BinaryExpression does not + implement abstract method getWidthWithoutTrivia + +INFO: MixedReturnStatement - src/Parser/Lexer.php:89 + Could not infer return type from $tokens + +------------------------------ + +81 errors found +99.34% type coverage +Psalm can help you fix these errors + +✓ Psalm analysis complete (with errors) +``` + +#### Error Levels + +| Level | Strictness | Description | +|-------|------------|-------------| +| 1 | Maximum | Catches almost everything | +| 2 | High | Practical maximum for most projects | +| **3** | **Medium** | **Recommended default** | +| 4 | Low | Loose type checking | +| 5+ | Very low | Minimal checks | + +#### Generate Baseline +```bash +make psalm-baseline + +# Creates psalm-baseline.xml +# Similar to PHPStan baseline +``` + +#### Taint Analysis (Security) +```bash +make psalm-taint + +# Detects security vulnerabilities: +# - SQL injection +# - XSS vulnerabilities +# - Command injection +# - Path traversal + +# Requires taint sources/sinks annotation +``` + +**Taint Analysis Example:** +```php +/** + * @psalm-taint-source input + */ +public function getUserInput(): string +{ + return $_GET['name']; +} + +/** + * @psalm-taint-sink html + */ +public function renderHTML(string $html): void +{ + echo $html; // Psalm warns: untrusted data in HTML context +} + +// Usage: +$input = $this->getUserInput(); +$this->renderHTML($input); // ⚠️ Taint detected! +``` + +#### Common Psalm Issues + +**Issue: Mixed Type** +```php +// ❌ Psalm error: MixedReturnStatement +public function getData() +{ + return $this->data; // Type unknown +} + +// ✅ Fixed +/** @return array */ +public function getData(): array +{ + return $this->data; +} +``` + +**Issue: Unimplemented Abstract Method** +```php +// ❌ Psalm error +abstract class Node +{ + abstract public function getChildren(): array; +} + +class Leaf extends Node +{ + // Missing implementation +} + +// ✅ Fixed +class Leaf extends Node +{ + public function getChildren(): array + { + return []; + } +} +``` + +### Combined Analysis +```bash +make analyse + +# Runs all static analysis tools: +# 1. PHPStan +# 2. Psalm +# 3. cs-check + +# Stops on first failure +# Use in pre-commit hooks +``` + +--- + +## Code Style & Formatting + +### Check Code Style + +#### PHPCS (PHP_CodeSniffer) +```bash +make cs-check + +# Executes: +# vendor/bin/phpcs --standard=phpcs.xml --colors src tests + +# Checks: +# - PSR-12 compliance +# - Naming conventions +# - Indentation +# - Line length +# - Spacing + +# Output: +FILE: /path/to/File.php +---------------------------------------------------------------------- +FOUND 5 ERRORS AFFECTING 3 LINES +---------------------------------------------------------------------- + 12 | ERROR | Line exceeds 120 characters + 15 | ERROR | Expected 1 space after comma; 0 found + 20 | ERROR | Missing doc comment for function +---------------------------------------------------------------------- +``` + +#### Configuration (phpcs.xml) +```xml + + + KaririCode Coding Standard + + + + + + + + + + + + + + src + tests + + + */vendor/* + */cache/* + +``` + +### Auto-fix Code Style + +#### Two-Stage Fixing +```bash +make format + +# Stage 1: PHP-CS-Fixer (modern PHP features) +# Stage 2: PHPCBF (PSR-12 compliance) + +# Output: +# → Formatting code... +# +# Loaded config default. +# Using cache file ".php-cs-fixer.cache". +# +# Fixed 12 files in 3.456 seconds, 18.00 MB memory used +# +# PHPCBF RESULT SUMMARY +# ---------------------------------------------------------------------- +# A TOTAL OF 5 FILES WERE FIXED +# ---------------------------------------------------------------------- +# +# ✓ Code formatted +``` + +#### PHP-CS-Fixer Configuration +```php +// .php-cs-fixer.php +in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') + ->exclude('vendor') + ->name('*.php'); + +return (new Config()) + ->setRules([ + '@PSR12' => true, + '@PHP84Migration' => true, + 'array_syntax' => ['syntax' => 'short'], + 'declare_strict_types' => true, + 'native_function_invocation' => [ + 'include' => ['@all'], + ], + 'ordered_imports' => [ + 'imports_order' => ['class', 'function', 'const'], + ], + 'no_unused_imports' => true, + ]) + ->setFinder($finder); +``` + +#### Preview Changes Without Applying +```bash +make format-dry + +# Shows diff without modifying files +# Review before applying changes +``` + +#### What Gets Fixed + +**Before Formatting:** +```php + + + + src + + + + + + + +``` + +### Coverage Thresholds +```xml + + + + + + + + + +``` + +--- + +## Mutation Testing + +### Run Mutation Tests +```bash +make mutation + +# Executes: +# XDEBUG_MODE=coverage vendor/bin/infection \ +# --threads=4 \ +# --min-msi=80 \ +# --min-covered-msi=90 \ +# --show-mutations + +# Requirements: +# - MSI (Mutation Score Indicator): ≥80% +# - Covered MSI: ≥90% + +# Output: +You are running Infection with xdebug enabled. + ____ ____ __ _ + / _/___ / __/__ _____/ /_(_)___ ____ + / // __ \/ /_/ _ \/ ___/ __/ / __ \/ __ \ + _/ // / / / __/ __/ /__/ /_/ / /_/ / / / / +/___/_/ /_/_/ \___/\___/\__/_/\____/_/ /_/ + +Running initial test suite... + +PHPUnit version: 11.5.2 + + 24 [============================] < 1 sec + +Generate mutants... + +Processing source code files: 21/21 +Creating mutated files and processes: 245/245 +.S.SSSS.S.S.S.S.S.S.S.S....S.S..S.S.S....S. + +245 mutations were generated: + 187 mutants were killed + 42 mutants were not covered by tests + 16 covered mutants were not detected + +Metrics: + Mutation Score Indicator (MSI): 82% + Mutation Code Coverage: 83% + Covered Code MSI: 92% +``` + +### Generate Detailed Report +```bash +make mutation-report + +# Generates infection.html +# Detailed mutation analysis +# Shows which mutations survived +``` + +### Mutation Types + +| Mutator | Description | Example | +|---------|-------------|---------| +| **Binary** | Changes operators | `+` → `-`, `&&` → `\|\|` | +| **Comparison** | Alters comparisons | `>` → `>=`, `==` → `!=` | +| **Increment** | Modifies increments | `++` → `--` | +| **Return Value** | Changes returns | `return true` → `return false` | +| **Array** | Mutates arrays | `[]` → `[null]` | +| **Function Call** | Removes calls | `trim($x)` → `$x` | + +### Mutation Testing Example + +**Original Code:** +```php +public function isPositive(int $number): bool +{ + return $number > 0; +} +``` + +**Test:** +```php +public function testIsPositive(): void +{ + $this->assertTrue($this->calculator->isPositive(5)); +} +``` + +**Mutation (survived):** +```php +// Mutator: GreaterThan → GreaterThanOrEqual +public function isPositive(int $number): bool +{ + return $number >= 0; // Mutation: > changed to >= +} +``` + +**Why it survived:** Test doesn't check boundary (0) + +**Better Test:** +```php +public function testIsPositive(): void +{ + $this->assertTrue($this->calculator->isPositive(5)); + $this->assertTrue($this->calculator->isPositive(1)); + $this->assertFalse($this->calculator->isPositive(0)); // ✅ Kills mutation + $this->assertFalse($this->calculator->isPositive(-5)); +} +``` + +--- + +## Troubleshooting + +### Issue 1: Tests Not Executing + +**Symptoms:** +```bash +$ make test + +There was 1 PHPUnit test runner warning: +1) XDEBUG_MODE=coverage has to be set +No tests executed! +``` + +**Cause:** Xdebug warning mistaken for error + +**Solutions:** + +**Option A: Disable Xdebug** +```bash +export XDEBUG_MODE=off +make test +``` + +**Option B: Use PHP INI** +```bash +php -d xdebug.mode=off vendor/bin/phpunit +``` + +**Option C: Docker (recommended)** +```bash +make docker-test +# Docker container has proper configuration +``` + +### Issue 2: PHPStan Memory Limit + +**Symptoms:** +```bash +$ make phpstan + +Fatal error: Allowed memory size of 134217728 bytes exhausted +``` + +**Solutions:** + +**Option A: Increase in Makefile (already done)** +```makefile +# Makefile.qa.mk already sets --memory-limit=512M +phpstan: + @$(PHPSTAN) analyse src --level=max --memory-limit=512M +``` + +**Option B: Increase further if needed** +```bash +vendor/bin/phpstan analyse src --level=max --memory-limit=1G +``` + +**Option C: Use PHP memory limit** +```bash +php -d memory_limit=1G vendor/bin/phpstan analyse src --level=max +``` + +### Issue 3: Psalm Cache Issues + +**Symptoms:** +```bash +$ make psalm + +InvalidArgumentException: Cache directory does not exist +``` + +**Solutions:** +```bash +# Clear Psalm cache +rm -rf var/cache/psalm + +# Run with --no-cache +vendor/bin/psalm --no-cache + +# Or use Makefile target (already includes --no-cache) +make psalm +``` + +### Issue 4: PHP-CS-Fixer Permission Denied + +**Symptoms:** +```bash +$ make format + +Permission denied: .php-cs-fixer.cache +``` + +**Solutions:** +```bash +# Remove cache file +rm .php-cs-fixer.cache + +# Fix permissions +chmod 644 .php-cs-fixer.cache + +# Or run without cache +vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --no-cache +``` + +### Issue 5: Coverage Generation Fails + +**Symptoms:** +```bash +$ make coverage + +PHP Fatal error: Xdebug is required for code coverage +``` + +**Solutions:** + +**Check Xdebug Installation:** +```bash +php -m | grep xdebug + +# If missing, install: +# Ubuntu/Debian +sudo apt install php8.4-xdebug + +# macOS (Homebrew) +brew install php@8.4 +pecl install xdebug +``` + +**Verify Configuration:** +```bash +php -i | grep xdebug.mode +# Should show: xdebug.mode => coverage => coverage +``` + +**Use Docker (recommended):** +```bash +make docker-coverage +# Pre-configured with Xdebug +``` + +--- + +## Best Practices + +### 1. Pre-Commit Quality Checks +```bash +# Install git hook +make git-hooks-setup + +# Or run manually before commit +make pre-commit + +# Runs: +# 1. format → Auto-fix style +# 2. lint → Syntax check +# 3. analyse → Static analysis +# 4. test-unit → Fast tests +``` + +### 2. Incremental Quality Improvement +```bash +# Week 1: Generate baselines +make phpstan-baseline +make psalm-baseline + +# Week 2-4: Fix issues incrementally +# (edit files, fix 10-20 issues per day) + +# Week 5: Regenerate baselines +make phpstan-baseline +make psalm-baseline + +# Track progress +git diff phpstan-baseline.neon +``` + +### 3. Test-Driven Development +```bash +# 1. Write failing test +vendor/bin/phpunit --filter testNewFeature +# ✗ FAILURES! + +# 2. Implement feature +# (edit source) + +# 3. Run test again +vendor/bin/phpunit --filter testNewFeature +# ✓ OK + +# 4. Run full suite +make test +``` + +### 4. Coverage-Driven Testing +```bash +# 1. Generate coverage +make coverage + +# 2. Open coverage/html/index.html +# Identify uncovered lines (red) + +# 3. Write tests for uncovered code + +# 4. Verify coverage improved +make coverage-text +``` + +### 5. Mutation-Driven Test Quality +```bash +# 1. Run mutation testing +make mutation + +# 2. Review survived mutants in infection.html + +# 3. Write tests to kill mutants + +# 4. Re-run until MSI ≥ 80% +make mutation +``` + +### 6. CI Integration Strategy +```yaml +# .github/workflows/qa.yml +name: Quality Assurance + +on: [push, pull_request] + +jobs: + qa: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: xdebug + + # Fast checks first + - name: Install + run: make install + + - name: Lint + run: make lint + + - name: Code Style + run: make cs-check + + - name: Static Analysis + run: make analyse + + - name: Tests + run: make test + + # Slow checks (if fast checks pass) + - name: Coverage + if: success() + run: make coverage + + - name: Mutation + if: success() + run: make mutation +``` + +--- + +## Command Reference + +### Testing +```bash +make test # Run all tests +make test-unit # Unit tests only +make test-integration # Integration tests +make test-functional # Functional tests +``` + +### Linting & Analysis +```bash +make lint # PHP syntax validation +make phpstan # PHPStan static analysis +make phpstan-baseline # Generate PHPStan baseline +make psalm # Psalm type checking +make psalm-baseline # Generate Psalm baseline +make psalm-taint # Security taint analysis +make analyse # All static analysis +``` + +### Code Style +```bash +make cs-check # Check code style (PHPCS) +make cbf-fix # Fix code style (PHPCBF) +make format # Auto-fix all style issues +make format-dry # Preview formatting changes +``` + +### Coverage & Mutation +```bash +make coverage # Generate coverage report +make coverage-text # Terminal coverage +make mutation # Mutation testing +make mutation-report # Detailed mutation report +``` + +--- + +## Quick Reference Card +``` +╔═══════════════════════════════════════════════════════════╗ +║ Quality Assurance Quick Reference ║ +╠═══════════════════════════════════════════════════════════╣ +║ LINT │ make lint ║ +║ FORMAT │ make format ║ +║ ANALYSE │ make analyse ║ +║ TEST │ make test ║ +║──────────────┼────────────────────────────────────────────║ +║ UNIT TESTS │ make test-unit ║ +║ COVERAGE │ make coverage ║ +║ MUTATION │ make mutation ║ +║──────────────┼────────────────────────────────────────────║ +║ PHPSTAN │ make phpstan ║ +║ PSALM │ make psalm ║ +║ CS CHECK │ make cs-check ║ +╚═══════════════════════════════════════════════════════════╝ + +Daily Workflow: + make format → make lint → make test-unit + +Pre-Commit: + make pre-commit (format + lint + analyse + test-unit) + +Full QA: + make analyse → make test → make coverage → make mutation +``` + +--- + +**Version**: 1.0.0 +**Module**: `Makefile.qa.mk` +**Maintainer**: Walmir Silva diff --git a/.docs/MAKEFILE-setup.md b/.docs/MAKEFILE-setup.md new file mode 100644 index 0000000..4a598f2 --- /dev/null +++ b/.docs/MAKEFILE-setup.md @@ -0,0 +1,1006 @@ +
+ +# Setup & Installation + +[![KaririCode](https://img.shields.io/badge/KaririCode-DevKit-6D00CC?style=for-the-badge)](https://github.com/KaririCode-Framework/kariricode-devkit) +[![PHP](https://img.shields.io/badge/PHP-8.4%2B-777BB4?style=for-the-badge&logo=php)](https://www.php.net) +[![Composer](https://img.shields.io/badge/Composer-2.x-885630?style=for-the-badge&logo=composer)](https://getcomposer.org) + +**kariricode/devkit** - Professional development environment for KaririCode Framework + +Part of the [KaririCode Framework](https://kariricode.org) ecosystem + +[Main Documentation](MAKEFILE.md) | [GitHub Repository](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Prerequisites](#prerequisites) +3. [Installation Methods](#installation-methods) +4. [Dependency Management](#dependency-management) +5. [Validation & Security](#validation--security) +6. [Cleanup Operations](#cleanup-operations) +7. [Troubleshooting](#troubleshooting) +8. [Best Practices](#best-practices) + +--- + +## Overview + +### Scope + +The setup module (`Makefile.setup.mk`) provides targets for: +- **PHP version validation** (8.4.0+ enforcement) +- **Dependency installation** (Composer-based) +- **Security auditing** (vulnerability scanning) +- **Environment verification** (tool availability) +- **Cleanup operations** (artifact removal) + +### Module Architecture +``` +Setup Module (.make/local/Makefile.setup.mk) +│ +├── Prerequisites +│ └── check-php → PHP version validation +│ +├── Installation +│ ├── install → Standard installation +│ ├── install-dev → Development installation +│ ├── fresh-install → Clean installation +│ └── update → Dependency updates +│ +├── Validation +│ ├── verify-install → Post-install verification +│ ├── validate → composer.json validation +│ ├── security → Vulnerability scan +│ ├── security-strict → Strict security audit +│ └── outdated → Outdated dependency check +│ +└── Cleanup + ├── clean → Remove artifacts + └── clean-all → Deep clean + vendor +``` + +--- + +## Prerequisites + +### System Requirements + +#### Minimum Requirements + +| Component | Minimum Version | Verification Command | +|-----------|-----------------|---------------------| +| **PHP** | 8.4.0+ | `php -v` | +| **Composer** | 2.0+ | `composer --version` | +| **Git** | 2.0+ | `git --version` | +| **Make** | 3.81+ | `make --version` | + +#### Required PHP Extensions +```bash +# Check installed extensions +php -m + +# Required: +- json # JSON processing +- mbstring # Multibyte string support +- xml # XML parsing +- tokenizer # PHP tokenization +- pcre # Regular expressions + +# Recommended: +- opcache # Performance optimization +- xdebug # Debugging & coverage +- redis # Caching support +``` + +### Verification + +#### Check PHP Version +```bash +# Validate PHP version requirement +make check-php + +# Output: +# → Checking PHP version... +# ✓ PHP version 8.4.14 OK (>= 8.4.0) +``` + +**Version Comparison Logic:** +```makefile +# From Makefile.functions.mk +CURRENT_VERSION="8.4.14" +MIN_VERSION="8.4.0" +LOWEST=$(printf '%s\n%s' "$MIN_VERSION" "$CURRENT_VERSION" | sort -V | head -n1) + +if [ "$LOWEST" != "$MIN_VERSION" ]; then + echo "✗ PHP 8.4.0+ required, found $CURRENT_VERSION" + exit 1 +fi +``` + +#### Debug Composer Configuration +```bash +# Show Composer paths and availability +make debug-composer + +# Output: +# COMPOSER_BIN = '/usr/local/bin/composer' +# COMPOSER = '/usr/local/bin/composer' +# Composer version 2.8.4 2024-12-12 + +# If not found: +# COMPOSER_BIN = '' +# COMPOSER = 'composer' +# Composer not found with command -v +``` + +#### Environment Information +```bash +# Show complete environment info +make info + +# Output: +# ╔════════════════════════════════════════════════════════╗ +# Project Information +# ──────────────────────────────────────────────────────── +# PHP Version: 8.4.14 +# PHP Binary: /usr/bin/php +# Composer: /usr/local/bin/composer +# Project Directory: /home/user/kariricode-devkit +# Source Directory: src +# Test Directory: tests +# +# Installed Tools +# ──────────────────────────────────────────────────────── +# PHPUnit: ✓ +# PHPStan: ✓ +# Psalm: ✓ +# PHPCS: ✓ +# PHP-CS-Fixer: ✓ +# Infection: ✓ +# PHPBench: ✓ +# ╚════════════════════════════════════════════════════════╝ +``` + +--- + +## Installation Methods + +### Standard Installation + +#### When to Use +- **First time setup** from existing `composer.lock` +- **After cloning** repository +- **Restoring** from version control + +#### Command +```bash +make install + +# Workflow: +# 1. Check PHP version (make check-php) +# 2. Validate composer.json +# 3. Install from composer.lock (if valid) +# 4. OR update if lock file outdated +# 5. Optimize autoloader +# 6. Verify installation +``` + +#### Output Example +```bash +$ make install + +→ Checking PHP version... +✓ PHP version 8.4.14 OK (>= 8.4.0) + +→ Installing Composer dependencies... +Loading composer repositories with package information +Installing dependencies from lock file (including require-dev) +Package operations: 42 installs, 0 updates, 0 removals + - Installing symfony/polyfill-php80 (v1.28.0): Extracting archive + - Installing psr/container (2.0.2): Extracting archive + ... +Generating optimized autoload files +✓ Installation complete +``` + +#### What Gets Installed +```bash +# Production dependencies +composer.json → require +├── php: ^8.4 +└── kariricode/* + +# Development tools +composer.json → require-dev +├── phpunit/phpunit: ^11.0 +├── phpstan/phpstan: ^2.0 +├── vimeo/psalm: ^5.0 +├── squizlabs/php_codesniffer: ^3.7 +├── friendsofphp/php-cs-fixer: ^3.0 +└── infection/infection: ^0.27 +``` + +### Development Installation + +#### When to Use +- **Development environment** setup +- **Contributing** to the project +- **Need all tools** for QA + +#### Command +```bash +make install-dev + +# Differences from 'install': +# - Preserves composer.lock without validation +# - Installs ALL dev dependencies +# - No autoloader optimization +# - Keeps debugging info +``` + +#### Use Case +```bash +# Scenario: Setting up development environment +git clone https://github.com/KaririCode-Framework/kariricode-devkit.git +cd kariricode-devkit + +# Install with all dev tools +make install-dev + +# Verify tools are available +make info +# All tools should show ✓ +``` + +### Fresh Installation + +#### When to Use +- **Corrupted** `composer.lock` +- **Dependency conflicts** +- **Major version updates** +- **Clean slate** needed + +#### Command +```bash +make fresh-install + +# Workflow: +# 1. Remove composer.lock +# 2. Install fresh dependencies +# 3. Generate new lock file +# 4. Optimize autoloader +# 5. Verify installation +``` + +⚠️ **Warning**: This regenerates `composer.lock` and may update packages to newer versions within version constraints. + +#### Output Example +```bash +$ make fresh-install + +→ Removing composer.lock... +→ Installing fresh dependencies... +No composer.lock file present. Updating dependencies to latest version. +Loading composer repositories with package information +Updating dependencies +Lock file operations: 42 installs, 0 updates, 0 removals + - Locking phpunit/phpunit (11.5.2) + - Locking phpstan/phpstan (2.1.5) + ... +Writing lock file +✓ Fresh installation complete +``` + +#### When to Commit New Lock File +```bash +# Review changes +git diff composer.lock + +# If intentional update: +git add composer.lock +git commit -m "chore: regenerate composer.lock" + +# If accidental: +git checkout composer.lock +make install # Restore from existing lock +``` + +--- + +## Dependency Management + +### Update Dependencies + +#### Update All Dependencies +```bash +make update + +# Executes: +# composer update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader + +# Updates: +# - Direct dependencies +# - Transitive dependencies +# - Respects version constraints in composer.json +``` + +#### Update Specific Package +```bash +# Use composer directly +make exec-php CMD="composer update vendor/package" + +# Example: +make exec-php CMD="composer update phpunit/phpunit" +``` + +#### Update with Constraints +```bash +# Update within patch versions +make exec-php CMD="composer update phpunit/phpunit --prefer-lowest" + +# Update with stability +make exec-php CMD="composer update --prefer-stable" +``` + +### Add New Dependencies +```bash +# Production dependency +make exec-php CMD="composer require vendor/package" + +# Development dependency +make exec-php CMD="composer require --dev vendor/package" + +# Example: Add new KaririCode component +make exec-php CMD="composer require kariricode/new-component" +``` + +### Remove Dependencies +```bash +# Remove package +make exec-php CMD="composer remove vendor/package" + +# Example: +make exec-php CMD="composer remove phpunit/phpunit" +``` + +### Check Outdated Packages +```bash +make outdated + +# Output shows direct dependencies needing updates: +# ╔════════════════════════════════════════════════════════╗ +# Direct dependencies required in composer.json: +# phpunit/phpunit 11.5.1 → 11.5.2 (patch update) +# phpstan/phpstan 2.1.3 → 2.1.5 (patch update) +# psalm/psalm 5.26.1 → 5.27.0 (minor update) +# ╚════════════════════════════════════════════════════════╝ + +# Ignores indirect dependencies by default +``` + +--- + +## Validation & Security + +### Validate composer.json + +#### Syntax & Structure Validation +```bash +make validate + +# Checks: +# - JSON syntax +# - Schema compliance +# - Required fields (name, description, license) +# - Version constraints syntax +# - PSR-4 autoload mappings + +# Output: +# → Validating composer.json... +# ./composer.json is valid +# ✓ composer.json is valid +``` + +#### Common Validation Errors + +**Invalid JSON:** +```json +{ + "name": "kariricode/devkit" + "description": "Missing comma" ← Error +} +``` + +**Invalid Version Constraint:** +```json +{ + "require": { + "php": "8.4" ← Should be "^8.4" or ">=8.4" + } +} +``` + +**Invalid PSR-4 Namespace:** +```json +{ + "autoload": { + "psr-4": { + "KaririCode\\": "src" ← Missing trailing backslash + } + } +} +``` + +### Security Auditing + +#### Standard Security Check +```bash +make security + +# Executes: +# composer audit --no-dev --locked + +# Checks for: +# - Known security vulnerabilities +# - CVEs in dependencies +# - Abandoned packages (warning only) +``` + +#### Output Examples + +**No Vulnerabilities:** +```bash +$ make security + +→ Checking for security vulnerabilities... +Found 0 security vulnerability advisories affecting 0 packages +✓ No security vulnerabilities found +``` + +**Vulnerabilities Found:** +```bash +$ make security + +→ Checking for security vulnerabilities... +Found 2 security vulnerability advisories affecting 1 package: + +symfony/http-kernel (v6.2.0) +├── CVE-2023-XXXXX (high severity) +│ Fixed in: 6.2.6 +│ Description: Information disclosure vulnerability +└── See: https://github.com/advisories/GHSA-xxxx-yyyy + +✗ Security vulnerabilities found +``` + +**Action Steps:** +```bash +# Update affected package +make exec-php CMD="composer update symfony/http-kernel" + +# Verify fix +make security +``` + +#### Abandoned Packages +```bash +$ make security + +⚠ Found abandoned packages (informational only): +───────────────────────────────────────────────── +Package: vendor/old-package +Replacement: vendor/new-package +───────────────────────────────────────────────── +✓ No security vulnerabilities found +``` + +#### Strict Security Mode +```bash +make security-strict + +# Differences from 'security': +# - Includes dev dependencies +# - Treats abandoned packages as errors +# - More verbose output +# - Fails on any issue + +# Use in: +# - CI/CD pipelines +# - Pre-release checks +# - Security-critical projects +``` + +--- + +## Cleanup Operations + +### Clean Build Artifacts +```bash +make clean + +# Removes: +# ├── build/ (Build outputs) +# ├── coverage/ (Code coverage reports) +# ├── reports/ (Static analysis reports) +# ├── var/cache/ (Application cache) +# ├── .phpunit.cache (PHPUnit cache) +# ├── .phpunit.result.cache +# ├── .php-cs-fixer.cache (CS Fixer cache) +# ├── infection.log (Mutation test logs) +# └── infection.html + +# Does NOT remove: +# - vendor/ (Dependencies) +# - composer.lock (Lock file) +# - Source code +``` + +#### When to Use + +- **Before commit** (remove temporary files) +- **Before benchmarks** (clean state) +- **Disk space** (reclaim space) +- **Fresh start** (consistent state) + +### Deep Clean +```bash +make clean-all + +# Removes everything from 'clean' PLUS: +# ├── vendor/ (All dependencies) +# └── composer.lock (Dependency lock file) + +# Requires 're-install' after: +# make install +``` + +⚠️ **Warning**: This removes `vendor/` and requires re-downloading all dependencies. + +#### When to Use + +- **Before fresh-install** +- **Switching branches** with different dependencies +- **Major PHP version** upgrades +- **Corrupted vendor** directory + +### Cleanup Workflow +```bash +# Daily development cleanup +make clean + +# Weekly deep cleanup +make clean-all +make install + +# Before release +make clean +make ci-full # Ensures clean build +``` + +--- + +## Troubleshooting + +### Issue 1: Composer Not Found + +**Symptoms:** +```bash +$ make install +COMPOSER_BIN = '' +COMPOSER = 'composer' +composer: command not found +``` + +**Diagnosis:** +```bash +make debug-composer +which composer +echo $PATH +``` + +**Solutions:** + +**Option A: Install Composer Globally** +```bash +# Download installer +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" + +# Verify installer (optional) +php -r "if (hash_file('sha384', 'composer-setup.php') === 'EXPECTED_HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" + +# Install globally +php composer-setup.php --install-dir=/usr/local/bin --filename=composer + +# Cleanup +php -r "unlink('composer-setup.php');" + +# Verify +composer --version +``` + +**Option B: Use Docker** +```bash +# Use Docker for Composer commands +make docker-composer CMD="install" +``` + +### Issue 2: PHP Version Mismatch + +**Symptoms:** +```bash +$ make check-php +✗ PHP 8.4.0+ required, found 8.3.12 +``` + +**Solutions:** + +**Option A: Install PHP 8.4 (Ubuntu/Debian)** +```bash +# Add PPA +sudo add-apt-repository ppa:ondrej/php +sudo apt update + +# Install PHP 8.4 +sudo apt install php8.4 php8.4-cli php8.4-{mbstring,xml,curl} + +# Set as default +sudo update-alternatives --set php /usr/bin/php8.4 + +# Verify +php -v +make check-php +``` + +**Option B: Use Docker** +```bash +# All commands in Docker with PHP 8.4 +make docker-install +make docker-ci +``` + +### Issue 3: Memory Limit Errors + +**Symptoms:** +```bash +$ make install +Fatal error: Allowed memory size of 134217728 bytes exhausted +``` + +**Solutions:** + +**Option A: Increase PHP Memory Limit** +```bash +# Temporary (single command) +php -d memory_limit=512M $(which composer) install + +# Permanent (php.ini) +echo "memory_limit = 512M" | sudo tee -a /etc/php/8.4/cli/php.ini + +# Verify +php -i | grep memory_limit +``` + +**Option B: Use Environment Variable** +```bash +# Set in .env +echo "COMPOSER_MEMORY_LIMIT=-1" >> .env + +# Or export globally +export COMPOSER_MEMORY_LIMIT=-1 +make install +``` + +### Issue 4: Lock File Out of Date + +**Symptoms:** +```bash +$ make install +Warning: The lock file is not up to date with the latest changes in composer.json +``` + +**Diagnosis:** +```bash +# Check what changed +composer validate --strict +``` + +**Solutions:** + +**Option A: Update Lock File** +```bash +# If intentional changes +make update + +# Commit new lock file +git add composer.lock +git commit -m "chore: update composer.lock" +``` + +**Option B: Restore composer.json** +```bash +# If accidental changes +git checkout composer.json +make install +``` + +### Issue 5: Authentication Required + +**Symptoms:** +```bash +$ make install +Authentication required (gitlab.com): +``` + +**Solutions:** + +**Option A: Add Auth Token** +```bash +# GitHub token +composer config --global github-oauth.github.com YOUR_TOKEN + +# GitLab token +composer config --global gitlab-oauth.gitlab.com YOUR_TOKEN + +# Verify +cat ~/.composer/auth.json +``` + +**Option B: SSH Keys** +```bash +# Use SSH instead of HTTPS +composer config --global use-github-api false + +# Ensure SSH key is added +ssh-add ~/.ssh/id_rsa +``` + +### Issue 6: Network Timeouts + +**Symptoms:** +```bash +$ make install + Failed to download symfony/http-kernel from dist: connection timed out +``` + +**Solutions:** + +**Option A: Increase Timeout** +```bash +# Increase process timeout +export COMPOSER_PROCESS_TIMEOUT=600 +make install +``` + +**Option B: Use Different Mirror** +```bash +# Configure packagist mirror +composer config --global repo.packagist composer https://packagist.com +``` + +**Option C: Retry** +```bash +# Sometimes transient network issues +make install # Try again +``` + +--- + +## Best Practices + +### 1. Version Control + +#### Commit These Files +```bash +✅ composer.json # Dependency definitions +✅ composer.lock # Locked versions +✅ Makefile # Build automation +✅ .make/ # Makefile modules +``` + +#### Ignore These Files +```bash +❌ vendor/ # Downloaded dependencies +❌ .phpunit.cache # Test cache +❌ .php-cs-fixer.cache # CS cache +❌ build/ # Build artifacts +❌ coverage/ # Coverage reports +``` + +**.gitignore Example:** +```gitignore +# Dependencies +/vendor/ + +# Build artifacts +/build/ +/coverage/ +/reports/ + +# Caches +/.phpunit.cache +/.phpunit.result.cache +/.php-cs-fixer.cache +/var/cache/ + +# Infection +infection.log +infection.html +``` + +### 2. Update Strategy + +#### Semantic Versioning Approach +```json +{ + "require": { + "kariricode/router": "^2.0", // Major: Breaking changes + "symfony/console": "~6.4.0", // Minor: New features + "psr/log": "3.0.*" // Patch: Bug fixes only + } +} +``` + +**Recommended Constraints:** +- `^2.0` - Allow minor and patch updates (2.0, 2.1, 2.1.1) +- `~2.1.0` - Allow patch updates only (2.1.0, 2.1.1, 2.1.2) +- `2.1.*` - Alias for `~2.1.0` +- `>=2.0 <3.0` - Explicit range + +#### Update Workflow +```bash +# Weekly: Check for updates +make outdated + +# Review release notes for each package +# https://github.com/vendor/package/releases + +# Update patch versions (safe) +make exec-php CMD="composer update --prefer-stable" + +# Test thoroughly +make ci-full + +# Commit if successful +git add composer.lock +git commit -m "chore: update dependencies (patch)" +``` + +### 3. Security Hygiene +```bash +# Daily (automated in CI) +make security + +# Weekly (manual review) +make outdated +make security-strict + +# Monthly (dependency audit) +make update +make ci-full +``` + +### 4. Environment Consistency + +#### Team Setup +```bash +# Document in README.md +## Requirements +- PHP 8.4+ +- Composer 2.x + +## Setup +make check-php +make install-dev +make info +``` + +#### CI/CD Setup +```yaml +# .github/workflows/ci.yml +- name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: xdebug + +- name: Validate Environment + run: make check-php + +- name: Install Dependencies + run: make install + +- name: Verify Installation + run: make verify-install +``` + +### 5. Cleanup Routine +```bash +# Before commit +make clean + +# Weekly +make clean +make install + +# Monthly (or disk space low) +make clean-all +make fresh-install +``` + +--- + +## Command Reference + +### Prerequisites +```bash +make check-php # Validate PHP version (8.4+) +make debug-composer # Debug Composer configuration +make info # Show environment information +``` + +### Installation +```bash +make install # Standard installation from lock +make install-dev # Development installation +make fresh-install # Clean installation (regenerates lock) +make update # Update dependencies +make verify-install # Verify installation success +``` + +### Validation & Security +```bash +make validate # Validate composer.json +make security # Security vulnerability scan +make security-strict # Strict security audit +make outdated # Check outdated dependencies +``` + +### Cleanup +```bash +make clean # Remove build artifacts +make clean-all # Deep clean (includes vendor/) +``` + +--- + +## Quick Reference Card +``` +╔═══════════════════════════════════════════════════════════╗ +║ Setup & Installation Quick Reference ║ +╠═══════════════════════════════════════════════════════════╣ +║ CHECK PHP │ make check-php ║ +║ INSTALL │ make install ║ +║ FRESH START │ make fresh-install ║ +║ UPDATE │ make update ║ +║──────────────┼────────────────────────────────────────────║ +║ VERIFY │ make verify-install ║ +║ VALIDATE │ make validate ║ +║ SECURITY │ make security ║ +║ OUTDATED │ make outdated ║ +║──────────────┼────────────────────────────────────────────║ +║ CLEAN │ make clean ║ +║ DEEP CLEAN │ make clean-all ║ +║ INFO │ make info ║ +╚═══════════════════════════════════════════════════════════╝ + +Daily Workflow: + make install → make clean → make test + +Weekly Workflow: + make outdated → make security → make update + +Fresh Start: + make clean-all → make fresh-install → make verify-install +``` + +--- + +**Version**: 1.0.0 +**Module**: `Makefile.setup.mk` +**Maintainer**: Walmir Silva diff --git a/.docs/MAKEFILE.md b/.docs/MAKEFILE.md new file mode 100644 index 0000000..9bd59d9 --- /dev/null +++ b/.docs/MAKEFILE.md @@ -0,0 +1,324 @@ +
+ +# KaririCode DevKit + +[![PHP Version](https://img.shields.io/badge/PHP-8.4%2B-777BB4?style=for-the-badge&logo=php)](https://www.php.net) +[![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?style=for-the-badge&logo=docker)](https://www.docker.com) +[![Make](https://img.shields.io/badge/Make-Automation-6D00CC?style=for-the-badge&logo=gnu)](https://www.gnu.org/software/make/) +[![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)](LICENSE) + +**Professional development environment for KaririCode Framework components** + +[Website](https://kariricode.org) | [Documentation](https://kariricode.org/docs) | [GitHub](https://github.com/KaririCode-Framework/kariricode-devkit) + +
+ +--- + +## 📚 Documentation Index + +| Document | Scope | Makefile Modules | +|----------|-------|------------------| +| **[Setup & Installation](MAKEFILE-setup.md)** | Dependency management, environment setup | `Makefile.setup.mk` | +| **[Quality Assurance](MAKEFILE-qa.md)** | Testing, linting, static analysis | `Makefile.qa.mk` | +| **[CI/CD Pipelines](MAKEFILE-pipeline.md)** | Orchestrated workflows, pre-commit hooks | `Makefile.orchestration.mk` | +| **[Development Helpers](MAKEFILE-helpers.md)** | Benchmarks, git hooks, release management | `Makefile.helpers.mk` | +| **[Docker Commands](MAKEFILE-docker.md)** | Docker execution, isolated environments | `Makefile.docker-*.mk` | +| **[Docker Compose](MAKEFILE-compose.md)** | Full stack environment management | `Makefile.docker-compose.mk` | + +--- + +## 🚀 Quick Start + +### First Time Setup +```bash +# 1. Check prerequisites +make check-php # Verify PHP 8.4+ + +# 2. Install dependencies +make install # Production dependencies +make install-dev # + Development tools + +# 3. Verify installation +make info # Show environment info +``` + +### Daily Development Workflow +```bash +# Local development +make format # Auto-fix code style +make test # Run tests +make analyse # Static analysis + +# Docker environment +make up # Start services +make test-compose # Integration tests +make down # Stop services +``` + +### CI/CD Integration +```bash +# Fast CI (1-2 min) +make ci # Essential checks + +# Full CI (3-5 min) +make ci-full # Complete validation + +# Docker CI (isolated) +make docker-ci # Consistent environment +``` + +--- + +## 📖 Architecture Overview + +### Module Organization +``` +.make/ +├── core/ # 🔧 Shared infrastructure +│ ├── Makefile.variables.mk # Colors, paths, tools +│ └── Makefile.functions.mk # Reusable shell functions +│ +├── local/ # 💻 Local development +│ ├── Makefile.setup.mk # Install, update, clean +│ ├── Makefile.qa.mk # Test, lint, analyse +│ └── Makefile.helpers.mk # Bench, hooks, stats +│ +├── pipeline/ # 🔄 CI/CD workflows +│ └── Makefile.orchestration.mk # Composed pipelines +│ +└── docker/ # 🐳 Docker execution + ├── Makefile.docker-core.mk # Shell, composer, php + ├── Makefile.docker-compose.mk # Service lifecycle + ├── Makefile.docker-qa.mk # QA in containers + ├── Makefile.docker-image.mk # Image management + └── Makefile.docker-tools.mk # Utilities +``` + +### Design Principles + +**Single Responsibility Principle (SRP)** +- Each `.mk` file has one clear purpose +- Setup ≠ QA ≠ Docker ≠ Pipeline + +**DRY (Don't Repeat Yourself)** +- Shared logic in `core/Makefile.functions.mk` +- Variables centralized in `core/Makefile.variables.mk` + +**Composability** +- Complex workflows built from simple targets +- `make ci` = lint + analyse + test +- `make cd` = ci-full + bench + release prep + +**Flexibility** +- Local execution: `make test` +- Docker execution: `make docker-test` +- Same interface, different environment + +--- + +## 🎯 Command Categories + +### By Frequency + +**Every Commit** +```bash +make format # Auto-fix style +make lint # Syntax check +make test-unit # Fast tests +``` + +**Before Push** +```bash +make ci # Full local CI +make analyse # Deep static analysis +``` + +**Weekly** +```bash +make update # Update dependencies +make security # Security audit +make outdated # Check for updates +``` + +**Release** +```bash +make cd # Complete validation +make tag VERSION=X.Y.Z # Create tag +``` + +### By Environment + +**Local Machine** +- Fast iteration +- IDE integration +- Custom configuration + +**Docker Container** +- Isolated environment +- Consistent results +- CI/CD simulation + +**Docker Compose** +- Full stack (PHP + Redis + Memcached) +- Integration tests +- Service dependencies + +--- + +## 📋 Command Reference + +### Essential Commands +```bash +# Help +make help # Show all commands +make -help # Module-specific help + +# Setup +make install # Install dependencies +make clean # Clean artifacts +make verify-install # Verify setup + +# Quality +make test # Run tests +make analyse # Static analysis +make format # Auto-fix code + +# Pipelines +make ci # Fast CI +make ci-full # Complete CI +make pre-commit # Quick checks + +# Docker +make docker-ci # CI in Docker +make up # Start compose +make down # Stop compose +``` + +### Getting Help +```bash +# General help +make help + +# Module-specific +make bench-help # Benchmark parameters + +# Debug +make info # Environment info +make debug-composer # Composer config +make env-check # Docker env vars +``` + +--- + +## 🔍 Troubleshooting + +### Quick Diagnostics +```bash +# Check environment +make info # PHP, Composer, tools +make check-php # PHP version check + +# Verify installation +make verify-install # Check dependencies + +# Docker issues +make docker-info # Docker environment +make validate-compose # docker-compose.yml syntax +make logs # Service logs +``` + +### Common Issues + +| Issue | Quick Fix | Documentation | +|-------|-----------|---------------| +| Tests not executing | `export XDEBUG_MODE=off` | [MAKEFILE-qa.md](MAKEFILE-qa.md#troubleshooting) | +| Port conflicts | Edit `.env`, change `APP_PORT` | [MAKEFILE-compose.md](MAKEFILE-compose.md#port-conflicts) | +| Composer errors | `make debug-composer` | [MAKEFILE-setup.md](MAKEFILE-setup.md#troubleshooting) | +| PHPStan errors | `make phpstan-baseline` | [MAKEFILE-qa.md](MAKEFILE-qa.md#static-analysis) | + +--- + +## 🎓 Learning Path + +### Beginner (Day 1) + +1. Read: [Setup & Installation](MAKEFILE-setup.md) +2. Run: `make install && make info` +3. Test: `make test` + +### Intermediate (Week 1) + +1. Read: [Quality Assurance](MAKEFILE-qa.md) +2. Setup: `make git-hooks-setup` +3. Practice: `make pre-commit` workflow + +### Advanced (Month 1) + +1. Read: [Docker Compose](MAKEFILE-compose.md) +2. Setup: `make up` +3. Integrate: `make test-compose` + +### Expert (Month 3) + +1. Read: [CI/CD Pipelines](MAKEFILE-pipeline.md) +2. Customize: Add project-specific targets +3. Optimize: Benchmark and tune + +--- + +## 🤝 Contributing + +### Adding New Targets + +1. **Choose the right module** + - Setup related? → `Makefile.setup.mk` + - Testing related? → `Makefile.qa.mk` + - Docker related? → `Makefile.docker-*.mk` + +2. **Follow conventions** +```makefile + target-name: ## Description for help + @echo "$(BLUE)→ Action...$(RESET)" + # implementation + @echo "$(GREEN)✓ Success$(RESET)" +``` + +3. **Document in corresponding .md file** + +4. **Test locally and in Docker** +```bash + make target-name + make docker- # if applicable +``` + +### Documentation Standards + +- Use **semantic organization** +- Include **working examples** +- Add **troubleshooting sections** +- Maintain **consistent formatting** +- Update **command reference tables** + +--- + +## 📞 Support + +### Resources + +- **Issues**: Found a bug? [Open an issue](https://github.com/KaririCode-Framework/kariricode-devkit/issues) +- **Discussions**: Questions? [Start a discussion](https://github.com/KaririCode-Framework/kariricode-devkit/discussions) +- **Documentation**: [Full documentation index](#-documentation-index) + +### Quick Links + +- [Prerequisites](MAKEFILE-setup.md#prerequisites) +- [Installation Guide](MAKEFILE-setup.md#installation) +- [Command Reference](#-command-reference) +- [Troubleshooting](#-troubleshooting) +- [Best Practices](MAKEFILE-pipeline.md#best-practices) + +--- + +**Version**: 1.0.0 +**Maintainer**: Walmir Silva diff --git a/.make/core/Makefile.functions.mk b/.make/core/Makefile.functions.mk new file mode 100644 index 0000000..60cafed --- /dev/null +++ b/.make/core/Makefile.functions.mk @@ -0,0 +1,66 @@ +# ============================================================================== +# KaririCode\DevKit - Reusable Functions +# ============================================================================== +# Define funções shell reutilizáveis seguindo DRY principle +# ============================================================================== + +# --- Validação de Parâmetros --- +define validate_param + @if [ -z "$($(1))" ]; then \ + echo "$(RED)✗ $(1) required. Usage: $(2)$(RESET)"; \ + exit 1; \ + fi +endef + +# --- Execução Docker Genérica --- +define docker_exec + @echo "$(BLUE)→ Running $(1) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) $(2) + @echo "$(GREEN)✓ $(1) complete$(RESET)" +endef + +define docker_exec_make + @echo "$(BLUE)→ Running make $(1) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) make $(1) + @echo "$(GREEN)✓ Docker make $(1) complete$(RESET)" +endef + +# --- Verificação de Arquivo --- +define check_file + @test -f $(1) || (echo "$(RED)✗ $(2) not found$(RESET)" && exit 1) +endef + +# --- Criação de Diretório --- +define ensure_dir + @mkdir -p $(1) +endef + +# --- Header de Pipeline --- +define pipeline_header + @echo "$(BOLD)$(CYAN)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo "$(BOLD)$(CYAN)║ $(1)$(RESET)" + @echo "$(BOLD)$(CYAN)╚════════════════════════════════════════════════════════╝$(RESET)" + @echo "" +endef + +# --- Verificação de Branch Git --- +define check_git_branch + @CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ + if [ "$$CURRENT_BRANCH" != "$(1)" ]; then \ + echo "$(RED)✗ This action requires branch '$(1)' (current: $$CURRENT_BRANCH)$(RESET)"; \ + exit 1; \ + fi +endef + +# --- Verificação de Versão PHP --- +define check_php_version + @echo "$(BLUE)→ Checking PHP version...$(RESET)"; \ + CURRENT_VERSION="$(PHP_VERSION)"; \ + MIN_VERSION="$(PHP_MIN_VERSION)"; \ + LOWEST=$$(printf '%s\n%s' "$$MIN_VERSION" "$$CURRENT_VERSION" | sort -V | head -n1); \ + if [ "$$LOWEST" != "$$MIN_VERSION" ]; then \ + echo "$(RED)✗ PHP $$MIN_VERSION+ required, found $$CURRENT_VERSION$(RESET)"; \ + exit 1; \ + fi; \ + echo "$(GREEN)✓ PHP version $$CURRENT_VERSION OK (>= $$MIN_VERSION)$(RESET)" +endef diff --git a/.make/core/Makefile.variables.mk b/.make/core/Makefile.variables.mk new file mode 100644 index 0000000..8fa271c --- /dev/null +++ b/.make/core/Makefile.variables.mk @@ -0,0 +1,58 @@ +# ============================================================================== +# KaririCode\DevKit - Core Variables & Configuration +# ============================================================================== +# Centraliza todas as variáveis compartilhadas entre módulos +# ============================================================================== + +# --- Cores & Formatação --- +RESET := \033[0m +BOLD := \033[1m +RED := \033[31m +GREEN := \033[32m +YELLOW := \033[33m +BLUE := \033[34m +MAGENTA := \033[35m +CYAN := \033[36m + +# --- Docker Configuration --- +DOCKER_IMAGE := kariricode/php-api-stack:dev +DOCKER_RUN := docker run --rm -v $(PWD):/var/www/html -w /var/www/html +DOCKER_RUN_IT := docker run --rm -it -v $(PWD):/var/www/html -w /var/www/html + +# --- PHP Configuration --- +PHP := $(shell which php) +PHP_VERSION := $(shell $(PHP) -r 'echo PHP_VERSION;') +PHP_MIN_VERSION := 8.4.0 +PHP_CLEAN_RUN := $(PHP) -d xdebug.mode=off -d pcov.enabled=0 -d opcache.enable=1 + +# --- Composer Configuration --- +# Prevent the 'COMPOSER' make variable from conflicting with the +# 'COMPOSER' environment variable used by the tool itself. +unexport COMPOSER +COMPOSER_BIN := $(shell command -v composer 2>/dev/null || echo "") +# Use the full path for execution. Fallback to 'composer' if not found. +COMPOSER := $(if $(COMPOSER_BIN),$(COMPOSER_BIN),composer) + +# --- Directories --- +SRC_DIR := src +TEST_DIR := tests +BENCHMARK_DIR := benchmarks +BUILD_DIR := build +COVERAGE_DIR := coverage +REPORTS_DIR := reports +CACHE_DIR := var/cache +BENCH_REPORT_DIR := $(BUILD_DIR)/benchmarks + +# --- Vendor Binaries --- +VENDOR_BIN := vendor/bin +PHPUNIT := $(VENDOR_BIN)/phpunit +PHPSTAN := $(VENDOR_BIN)/phpstan +PSALM := $(VENDOR_BIN)/psalm +PHPCS := $(VENDOR_BIN)/phpcs +PHPCBF := $(VENDOR_BIN)/phpcbf +PHP_CS_FIXER := $(VENDOR_BIN)/php-cs-fixer +INFECTION := $(VENDOR_BIN)/infection +PHPBENCH := $(VENDOR_BIN)/phpbench + +# --- Export all for subshells --- +export diff --git a/.make/docker/Makefile.docker-compose.mk b/.make/docker/Makefile.docker-compose.mk new file mode 100644 index 0000000..d5a8696 --- /dev/null +++ b/.make/docker/Makefile.docker-compose.mk @@ -0,0 +1,200 @@ +# ============================================================================== +# KaririCode\DevKit - Docker Compose Management +# ============================================================================== +# Gerencia o ciclo de vida completo do ambiente Docker Compose +# ============================================================================== + +.PHONY: up down restart stop start status logs logs-follow ps \ + exec-php exec-memcached health config validate-compose \ + up-build rebuild prune network-inspect + +# ============================================================================== +# LIFECYCLE MANAGEMENT +# ============================================================================== + +up: ## Start Docker Compose services + @echo "$(BLUE)→ Starting Docker Compose services...$(RESET)" + @if [ ! -f .env ]; then \ + echo "$(YELLOW)⚠ .env not found, copying from .env.example...$(RESET)"; \ + cp .env.example .env 2>/dev/null || echo "$(RED)✗ .env.example not found$(RESET)"; \ + fi + @docker compose up -d + @echo "$(GREEN)✓ Services started$(RESET)" + @$(MAKE) --no-print-directory status + +up-build: ## Start services with build + @echo "$(BLUE)→ Building and starting Docker Compose services...$(RESET)" + @docker compose up -d --build + @echo "$(GREEN)✓ Services built and started$(RESET)" + @$(MAKE) --no-print-directory status + +down: ## Stop and remove Docker Compose services + @echo "$(BLUE)→ Stopping Docker Compose services...$(RESET)" + @docker compose down + @echo "$(GREEN)✓ Services stopped and removed$(RESET)" + +stop: ## Stop Docker Compose services (without removing) + @echo "$(BLUE)→ Stopping Docker Compose services...$(RESET)" + @docker compose stop + @echo "$(GREEN)✓ Services stopped$(RESET)" + +start: ## Start existing Docker Compose services + @echo "$(BLUE)→ Starting existing Docker Compose services...$(RESET)" + @docker compose start + @echo "$(GREEN)✓ Services started$(RESET)" + +restart: ## Restart Docker Compose services + @echo "$(BLUE)→ Restarting Docker Compose services...$(RESET)" + @docker compose restart + @echo "$(GREEN)✓ Services restarted$(RESET)" + +rebuild: down clean-volumes up-build ## Rebuild environment from scratch + @echo "$(GREEN)✓ Environment rebuilt$(RESET)" + +# ============================================================================== +# MONITORING & INSPECTION +# ============================================================================== + +status: ## Show services status + @echo "$(BOLD)$(CYAN)Docker Compose Services Status$(RESET)" + @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker compose ps + @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + +ps: status ## Alias for status + +logs: ## Show logs from all services (usage: make logs SERVICE=php) + @if [ -n "$(SERVICE)" ]; then \ + echo "$(BLUE)→ Showing logs for service: $(SERVICE)$(RESET)"; \ + docker compose logs $(SERVICE); \ + else \ + echo "$(BLUE)→ Showing logs for all services$(RESET)"; \ + docker compose logs; \ + fi + +logs-follow: ## Follow logs from services (usage: make logs-follow SERVICE=php) + @if [ -n "$(SERVICE)" ]; then \ + echo "$(BLUE)→ Following logs for service: $(SERVICE)$(RESET)"; \ + docker compose logs -f $(SERVICE); \ + else \ + echo "$(BLUE)→ Following logs for all services$(RESET)"; \ + docker compose logs -f; \ + fi + +health: ## Check services health status + @echo "$(BOLD)$(CYAN)Services Health Check$(RESET)" + @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker compose ps --format json | jq -r '.[] | "\(.Name): \(.Health // "N/A") - \(.State)"' + @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + +# ============================================================================== +# CONTAINER INTERACTION +# ============================================================================== + +exec-php: ## Execute command in PHP container (usage: make exec-php CMD="php -v") + @if [ -z "$(CMD)" ]; then \ + echo "$(BLUE)→ Opening interactive shell in PHP container...$(RESET)"; \ + docker compose exec php /bin/bash; \ + else \ + echo "$(BLUE)→ Executing: $(CMD)$(RESET)"; \ + docker compose exec php $(CMD); \ + fi + +exec-memcached: ## Execute command in Memcached container + @echo "$(BLUE)→ Connecting to Memcached container...$(RESET)" + @docker compose exec memcached sh + +# ============================================================================== +# CONFIGURATION & VALIDATION +# ============================================================================== + +config: ## Validate and view Docker Compose configuration + @echo "$(BLUE)→ Validating Docker Compose configuration...$(RESET)" + @docker compose config + @echo "$(GREEN)✓ Configuration is valid$(RESET)" + +validate-compose: ## Validate docker-compose.yml syntax + @echo "$(BLUE)→ Validating docker-compose.yml...$(RESET)" + @docker compose config --quiet && \ + echo "$(GREEN)✓ docker-compose.yml is valid$(RESET)" || \ + (echo "$(RED)✗ docker-compose.yml has errors$(RESET)" && exit 1) + +# ============================================================================== +# NETWORK & RESOURCES +# ============================================================================== + +network-inspect: ## Inspect Docker network + @echo "$(BOLD)$(CYAN)Docker Network Information$(RESET)" + @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker network inspect $$(docker compose config --format json | jq -r '.networks | keys[0]') 2>/dev/null || \ + echo "$(YELLOW)⚠ Network not found or not created yet$(RESET)" + @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + +prune: ## Remove unused Docker resources + @echo "$(YELLOW)⚠ This will remove all unused containers, networks, and volumes$(RESET)" + @echo "$(BLUE)→ Pruning Docker resources...$(RESET)" + @docker system prune -f --volumes + @echo "$(GREEN)✓ Docker resources pruned$(RESET)" + +clean-volumes: ## Remove all volumes (WARNING: data loss) + @echo "$(RED)⚠ WARNING: This will delete ALL volume data!$(RESET)" + @read -p "Are you sure? [y/N] " -n 1 -r; \ + echo; \ + if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ + echo "$(BLUE)→ Removing all volumes...$(RESET)"; \ + docker compose down -v; \ + echo "$(GREEN)✓ Volumes removed$(RESET)"; \ + else \ + echo "$(YELLOW)Cancelled$(RESET)"; \ + fi + +# ============================================================================== +# QUICK ACTIONS +# ============================================================================== + +shell: exec-php ## Alias for exec-php (open shell) + +composer-install: ## Run composer install in PHP container + @echo "$(BLUE)→ Running composer install...$(RESET)" + @docker compose exec php composer install --no-interaction --prefer-dist --optimize-autoloader + @echo "$(GREEN)✓ Composer dependencies installed$(RESET)" + +composer-update: ## Run composer update in PHP container + @echo "$(BLUE)→ Running composer update...$(RESET)" + @docker compose exec php composer update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader + @echo "$(GREEN)✓ Composer dependencies updated$(RESET)" + +test-compose: up ## Start services and run tests + @echo "$(BLUE)→ Waiting for services to be ready...$(RESET)" + @sleep 3 + @$(MAKE) --no-print-directory exec-php CMD="make test" + +# ============================================================================== +# DIAGNOSTIC & TROUBLESHOOTING +# ============================================================================== + +ports: ## Show exposed ports + @echo "$(BOLD)$(CYAN)Exposed Ports$(RESET)" + @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker compose ps --format "table {{.Name}}\t{{.Ports}}" + @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + +inspect-php: ## Inspect PHP container + @echo "$(BOLD)$(CYAN)PHP Container Inspection$(RESET)" + @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker compose exec php php -v + @echo "" + @docker compose exec php php -m + @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + +env-check: ## Verify environment variables + @echo "$(BOLD)$(CYAN)Environment Configuration$(RESET)" + @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @if [ -f .env ]; then \ + echo "$(GREEN)✓ .env file exists$(RESET)"; \ + grep -E '^[A-Z_]+=' .env | head -20; \ + else \ + echo "$(RED)✗ .env file not found$(RESET)"; \ + echo "$(YELLOW) Run: cp .env.example .env$(RESET)"; \ + fi + @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" \ No newline at end of file diff --git a/.make/docker/Makefile.docker-core.mk b/.make/docker/Makefile.docker-core.mk new file mode 100644 index 0000000..4fb142a --- /dev/null +++ b/.make/docker/Makefile.docker-core.mk @@ -0,0 +1,25 @@ +# ============================================================================== +# KaririCode\DevKit - Docker Core Functions +# ============================================================================== +# Abstrai lógica compartilhada de execução Docker +# ============================================================================== + +.PHONY: docker-shell docker-composer docker-php + +# ============================================================================== +# CORE DOCKER ENTRYPOINTS +# ============================================================================== + +docker-shell: ## Open interactive shell in Docker + @echo "$(BLUE)→ Opening Docker shell ($(DOCKER_IMAGE))...$(RESET)" + @$(DOCKER_RUN_IT) $(DOCKER_IMAGE) /bin/bash + +docker-composer: ## Run composer in Docker (usage: make docker-composer CMD="install") + $(call validate_param,CMD,make docker-composer CMD='install') + @echo "$(BLUE)→ Running composer $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) composer $(CMD) + +docker-php: ## Run PHP command in Docker (usage: make docker-php CMD="-v") + $(call validate_param,CMD,make docker-php CMD='-v') + @echo "$(BLUE)→ Running php $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) php $(CMD) diff --git a/.make/docker/Makefile.docker-image.mk b/.make/docker/Makefile.docker-image.mk new file mode 100644 index 0000000..37f598f --- /dev/null +++ b/.make/docker/Makefile.docker-image.mk @@ -0,0 +1,41 @@ +# ============================================================================== +# KaririCode\DevKit - Docker Image Management +# ============================================================================== +# +# Provides targets for managing the Docker image lifecycle, such as +# pulling, inspecting, and cleaning. +# +# Usage: +# make docker-pull +# make docker-info +# +# Author: Walmir Silva +# ============================================================================== + +# ============================================================================== +# DOCKER IMAGE TARGETS +# ============================================================================== + +.PHONY: docker-pull docker-info docker-clean + +docker-pull: ## Pull Docker image from registry + @echo "$(BLUE)→ Pulling Docker image $(DOCKER_IMAGE)...$(RESET)" + @docker pull $(DOCKER_IMAGE) + @echo "$(GREEN)✓ Docker image pulled$(RESET)" + +docker-info: ## Show Docker environment info + @echo "$(BOLD)$(CYAN)Docker Environment Information$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo "Docker Image: $(DOCKER_IMAGE)" + @echo "Mount Point: $(PWD):/app" + @echo "" + @echo "$(BOLD)$(CYAN)Container PHP Info$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) php -v + @echo "" + @$(DOCKER_RUN) $(DOCKER_IMAGE) composer --version + +docker-clean: ## Clean Docker resources + @echo "$(BLUE)→ Cleaning Docker resources...$(RESET)" + @docker system prune -f + @echo "$(GREEN)✓ Docker cleanup complete$(RESET)" diff --git a/.make/docker/Makefile.docker-qa.mk b/.make/docker/Makefile.docker-qa.mk new file mode 100644 index 0000000..b99f9a5 --- /dev/null +++ b/.make/docker/Makefile.docker-qa.mk @@ -0,0 +1,64 @@ +# ============================================================================== +# KaririCode\DevKit - Docker CI/QA Pipeline +# ============================================================================== +# Pipeline completo de CI/QA em ambiente Docker isolado +# ============================================================================== + +.PHONY: docker-test docker-test-unit docker-test-integration docker-coverage \ + docker-analyse docker-phpstan docker-psalm docker-cs-check \ + docker-format docker-lint docker-ci docker-ci-full docker-bench + +# ============================================================================== +# DOCKER QA TARGETS (Individual) +# ============================================================================== + +docker-test: ## Run tests in Docker + $(call docker_exec_make,test) + +docker-test-unit: ## Run unit tests in Docker + $(call docker_exec_make,test-unit) + +docker-test-integration: ## Run integration tests in Docker + $(call docker_exec_make,test-integration) + +docker-coverage: ## Generate coverage in Docker + @echo "$(BLUE)→ Generating coverage in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) make coverage + @echo "$(GREEN)✓ Coverage report: $(COVERAGE_DIR)/html/index.html$(RESET)" + +docker-analyse: ## Run static analysis in Docker + $(call docker_exec_make,analyse) + +docker-phpstan: ## Run PHPStan in Docker + $(call docker_exec_make,phpstan) + +docker-psalm: ## Run Psalm in Docker + $(call docker_exec_make,psalm) + +docker-cs-check: ## Check coding standards in Docker + $(call docker_exec_make,cs-check) + +docker-format: ## Format code in Docker + $(call docker_exec_make,format) + +docker-lint: ## Lint PHP files in Docker + $(call docker_exec_make,lint) + +# ============================================================================== +# DOCKER CI PIPELINES (Orchestration) +# ============================================================================== + +docker-ci: ## Run CI pipeline in Docker + $(call pipeline_header,"Docker CI Pipeline (Isolated Environment) ") + $(call docker_exec_make,ci) + @echo "" + @echo "$(BOLD)$(GREEN)✓ Docker CI pipeline completed$(RESET)" + +docker-ci-full: ## Run full CI pipeline in Docker + $(call pipeline_header,"Docker Full CI Pipeline (Isolated Environment) ") + $(call docker_exec_make,ci-full) + @echo "" + @echo "$(BOLD)$(GREEN)✓ Docker full CI pipeline completed$(RESET)" + +docker-bench: ## Run benchmarks in Docker + $(call docker_exec_make,bench) diff --git a/.make/docker/Makefile.docker-tools.mk b/.make/docker/Makefile.docker-tools.mk new file mode 100644 index 0000000..f9862ac --- /dev/null +++ b/.make/docker/Makefile.docker-tools.mk @@ -0,0 +1,71 @@ +# ============================================================================== +# KaririCode\DevKit - Docker Development Tools +# ============================================================================== +# Ferramentas de desenvolvimento em ambiente Docker +# ============================================================================== + +.PHONY: docker-htop docker-vim docker-less docker-jq docker-yq \ + docker-lsof docker-strace docker-ip docker-nc + +# ============================================================================== +# INTERACTIVE TOOLS +# ============================================================================== + +docker-htop: ## Run htop utility in Docker + @echo "$(BLUE)→ Running htop in Docker...$(RESET)" + @$(DOCKER_RUN_IT) $(DOCKER_IMAGE) htop + +# ============================================================================== +# TEXT EDITORS & VIEWERS +# ============================================================================== + +docker-vim: ## Edit files with vim in Docker (usage: make docker-vim CMD="src/file.php") + $(call validate_param,CMD,make docker-vim CMD='src/file.php') + @echo "$(BLUE)→ Running vim $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN_IT) $(DOCKER_IMAGE) vim $(CMD) + +docker-less: ## View files with less in Docker (usage: make docker-less CMD="README.md") + $(call validate_param,CMD,make docker-less CMD='README.md') + @echo "$(BLUE)→ Running less $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN_IT) $(DOCKER_IMAGE) less $(CMD) + +# ============================================================================== +# DATA PROCESSING TOOLS +# ============================================================================== + +docker-jq: ## Run jq utility in Docker (usage: make docker-jq CMD="'.version' composer.json") + $(call validate_param,CMD,make docker-jq CMD="'.version' composer.json") + @echo "$(BLUE)→ Running jq $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) sh -c "jq $(CMD)" + +docker-yq: ## Run yq utility in Docker (usage: make docker-yq CMD="'.services' docker-compose.yml") + $(call validate_param,CMD,make docker-yq CMD="'.services' docker-compose.yml") + @echo "$(BLUE)→ Running yq $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) sh -c "yq $(CMD)" + +# ============================================================================== +# SYSTEM DIAGNOSTIC TOOLS +# ============================================================================== + +docker-lsof: ## Run lsof utility in Docker (usage: make docker-lsof CMD="-i") + @echo "$(BLUE)→ Running lsof $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN_IT) --cap-add=SYS_PTRACE --cap-add=SYS_ADMIN $(DOCKER_IMAGE) lsof $(CMD) + +docker-strace: ## Run strace utility in Docker (usage: make docker-strace CMD="php -v") + $(call validate_param,CMD,make docker-strace CMD='php -v') + @echo "$(BLUE)→ Running strace $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN_IT) --cap-add=SYS_PTRACE $(DOCKER_IMAGE) strace $(CMD) + +# ============================================================================== +# NETWORK DIAGNOSTIC TOOLS +# ============================================================================== + +docker-ip: ## Run iproute2 utility in Docker (usage: make docker-ip CMD="addr") + $(call validate_param,CMD,make docker-ip CMD='addr') + @echo "$(BLUE)→ Running ip $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN) $(DOCKER_IMAGE) ip $(CMD) + +docker-nc: ## Run netcat utility in Docker (usage: make docker-nc CMD="-vz localhost 9000") + $(call validate_param,CMD,make docker-nc CMD='-vz localhost 9000') + @echo "$(BLUE)→ Running netcat $(CMD) in Docker...$(RESET)" + @$(DOCKER_RUN_IT) $(DOCKER_IMAGE) nc $(CMD) diff --git a/.make/local/Makefile.helpers.mk b/.make/local/Makefile.helpers.mk new file mode 100644 index 0000000..cafd966 --- /dev/null +++ b/.make/local/Makefile.helpers.mk @@ -0,0 +1,221 @@ +# ============================================================================== +# KaririCode\DevKit - Developer Helper Targets +# ============================================================================== + +.PHONY: bench bench-help git-hooks-setup git-hooks-remove \ + git-hooks-check watch-test server shell info tag release stats loc + +# ============================================================================== +# BENCHMARKING & PERFORMANCE (single target, param-driven) +# ============================================================================== + +# --- Tooling & Config --- +PHPBENCH ?= vendor/bin/phpbench +# This is the correct way: execute the phpbench script *with* a clean PHP binary. +PHP_CLEAN_RUN := $(PHP) -d xdebug.mode=off -d pcov.enabled=0 -d opcache.enable=1 + +# --- Directories --- +BENCHMARK_DIR ?= benchmarks +BENCH_REPORT_DIR ?= $(BUILD_DIR)/benchmarks + +# --- Default Parameters (Trimmed whitespace) --- +REF ?= auto +STORE ?= 0 +TAG ?= +ENFORCE_MAIN ?= 0 +REPORT ?= 0 + +# --- Encapsulated Benchmark Script --- +# This "encapsulates" the shell logic to hide command echoing +define BENCH_SCRIPT + @set -e; \ + BENCH_FLAGS="--progress=dots"; \ + \ + # ------------------ Comparison (REF) ------------------ + if [ "$(REF)" = "auto" ]; then \ + if $(PHP_CLEAN_RUN) $(PHPBENCH) log | grep -E -q "Tag:[[:space:]]+main"; then \ + printf "$(GREEN)✓ 'main' reference found. Enabling comparison…$(RESET)\n"; \ + BENCH_FLAGS="$$BENCH_FLAGS --ref=main"; \ + else \ + printf "$(YELLOW)⚠ No 'main' reference found. Running without comparison.$(RESET)\n"; \ + printf "$(YELLOW) Hint: make bench STORE=1 TAG=main ENFORCE_MAIN=1$(RESET)\n"; \ + fi; \ + elif [ -n "$(REF)" ]; then \ + printf "$(CYAN)→ Comparing against reference: %s$(RESET)\n" "$(REF)"; \ + BENCH_FLAGS="$$BENCH_FLAGS --ref=$(REF)"; \ + fi; \ + \ + # ------------------ Execution / Storage ------------------ + if [ "$(STORE)" = "1" ]; then \ + if [ -z "$(TAG)" ]; then \ + printf "$(RED)✗ TAG is required when STORE=1 (e.g., TAG=my-feature)$(RESET)\n"; \ + exit 1; \ + fi; \ + if [ "$(TAG)" = "main" ] && [ "$(ENFORCE_MAIN)" = "1" ]; then \ + CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ + if [ "$$CURRENT_BRANCH" != "main" ]; then \ + printf "$(RED)✗ This action requires branch 'main' (current: %s)$(RESET)\n" "$$CURRENT_BRANCH"; \ + exit 1; \ + fi; \ + fi; \ + printf "$(BLUE)→ Storing run with tag '%s'…$(RESET)\n" "$(TAG)"; \ + if [ "$(REPORT)" = "1" ]; then \ + $(PHP_CLEAN_RUN) $(PHPBENCH) run $$BENCH_FLAGS --store --tag="$(TAG)" | tee "$(BENCH_REPORT_DIR)/last.txt"; \ + printf "$(GREEN)✓ Output saved to %s/last.txt$(RESET)\n" "$(BENCH_REPORT_DIR)"; \ + else \ + $(PHP_CLEAN_RUN) $(PHPBENCH) run $$BENCH_FLAGS --store --tag="$(TAG)"; \ + fi; \ + else \ + if [ "$(REPORT)" = "1" ]; then \ + $(PHP_CLEAN_RUN) $(PHPBENCH) run $$BENCH_FLAGS | tee "$(BENCH_REPORT_DIR)/last.txt"; \ + printf "$(GREEN)✓ Output saved to %s/last.txt$(RESET)\n" "$(BENCH_REPORT_DIR)"; \ + else \ + $(PHP_CLEAN_RUN) $(PHPBENCH) run $$BENCH_FLAGS; \ + fi; \ + fi; \ + \ + printf "$(GREEN)✓ Benchmarks complete$(RESET)\n" +endef + +bench: ## Run benchmarks (unified target). See 'make bench-help'. + @printf "$(BLUE)→ Running benchmarks (unified target)…$(RESET)\n" + @mkdir -p "$(BENCHMARK_DIR)" + @mkdir -p "$(BENCH_REPORT_DIR)" + @$(BENCH_SCRIPT) + +bench-help: ## Show usage help for the unified bench command + @echo "$(BOLD)$(CYAN)Benchmark Command Help$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo "Single entrypoint: $(BOLD)make bench$(RESET)" + @echo "" + @echo "$(BOLD)Parameters:$(RESET)" + @echo " $(CYAN)REF=auto|main|$(RESET) Compare against a stored tag" + @echo " auto → try 'main' if available (default)" + @echo " main → compare against main reference" + @echo " → compare against any other tag" + @echo "" + @echo " $(CYAN)STORE=1 TAG=$(RESET) Store benchmarks with a given tag" + @echo " ex.: STORE=1 TAG=feature-x" + @echo "" + @echo " $(CYAN)ENFORCE_MAIN=1$(RESET) When TAG=main, ensures you are on branch 'main'" + @echo "" + @echo " $(CYAN)REPORT=1$(RESET) Save text output to $(BENCH_REPORT_DIR)/last.txt" + @echo "" + @echo "$(BOLD)Examples:$(RESET)" + @echo " make bench # Run normal benchmarks" + @echo " make bench REF=main # Compare against 'main'" + @echo " make bench REF=my-tag # Compare against custom tag" + @echo " make bench STORE=1 TAG=feat # Store results under tag 'feat'" + @echo " make bench STORE=1 TAG=main ENFORCE_MAIN=1 # Store results as 'main'" + @echo " make bench REF=main REPORT=1 # Compare and save output to last.txt" + @echo "" + @echo "$(GREEN)✓ Tip: Run 'make bench-help' anytime to see this guide$(RESET)" + +# ============================================================================== +# DEVELOPMENT HELPERS +# ============================================================================== +git-hooks-setup: ## Setup git hooks for development workflow + @echo "$(BLUE)→ Setting up git hooks...$(RESET)" + @mkdir -p .git/hooks + @if [ -f .git/hooks/pre-commit ] && [ ! -f .git/hooks/pre-commit.bak ]; then \ + echo "$(YELLOW)⚠ Existing pre-commit hook found. Backing up...$(RESET)"; \ + mv .git/hooks/pre-commit .git/hooks/pre-commit.bak; \ + fi + @echo '#!/bin/sh' > .git/hooks/pre-commit + @echo 'set -e' >> .git/hooks/pre-commit + @echo 'make pre-commit' >> .git/hooks/pre-commit + @chmod +x .git/hooks/pre-commit + @echo "$(GREEN)✓ Git hooks set up$(RESET)" + +git-hooks-remove: ## Remove git hooks and restore backups if any + @echo "$(BLUE)→ Cleaning up git hooks...$(RESET)" + @if [ -f .git/hooks/pre-commit.bak ]; then \ + echo "$(YELLOW)↩ Restoring backup pre-commit hook...$(RESET)"; \ + mv .git/hooks/pre-commit.bak .git/hooks/pre-commit; \ + elif [ -f .git/hooks/pre-commit ]; then \ + echo "$(RED)✗ Removing generated pre-commit hook...$(RESET)"; \ + rm .git/hooks/pre-commit; \ + else \ + echo "$(YELLOW)⚠ No pre-commit hook found$(RESET)"; \ + fi + @echo "$(GREEN)✓ Git hooks cleaned$(RESET)" + +git-hooks-check: ## Check if git hooks are installed correctly + @echo "$(BLUE)→ Verifying git hooks...$(RESET)" + @if [ -f .git/hooks/pre-commit ]; then \ + if grep -q "make pre-commit" .git/hooks/pre-commit; then \ + echo "$(GREEN)✓ pre-commit hook is installed correctly$(RESET)"; \ + else \ + echo "$(RED)✗ pre-commit hook exists but was not installed by this Makefile$(RESET)"; \ + fi \ + else \ + echo "$(RED)✗ pre-commit hook not found$(RESET)"; \ + fi + +info: ## Show PHP and project information + @echo "$(BOLD)$(CYAN)Project Information$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo "PHP Version: $(PHP_VERSION)" + @echo "PHP Binary: $(PHP)" + @echo "Composer: $(COMPOSER)" + @echo "Project Directory: $(shell pwd)" + @echo "Source Directory: $(SRC_DIR)" + @echo "Test Directory: $(TEST_DIR)" + @echo "" + @echo "$(BOLD)$(CYAN)Installed Tools$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @test -f $(PHPUNIT) && echo "PHPUnit: ✓" || echo "PHPUnit: ✗" + @test -f $(PHPSTAN) && echo "PHPStan: ✓" || echo "PHPStan: ✗" + @test -f $(PSALM) && echo "Psalm: ✓" || echo "Psalm: ✗" + @test -f $(PHPCS) && echo "PHPCS: ✓" || echo "PHPCS: ✗" + @test -f $(PHP_CS_FIXER) && echo "PHP-CS-Fixer: ✓" || echo "PHP-CS-Fixer: ✗" + @test -f $(INFECTION) && echo "Infection: ✓" || echo "Infection: ✗" + @test -f $(PHPBENCH) && echo "PHPBench: ✓" || echo "PHPBench: ✗" + +# ============================================================================== +# RELEASE MANAGEMENT +# ============================================================================== + +tag: ## Create a new git tag (usage: make tag VERSION=1.0.0) + @if [ -z "$(VERSION)" ]; then \ + echo "$(RED)✗ VERSION is required. Usage: make tag VERSION=1.0.0$(RESET)"; \ + exit 1; \ + fi + @echo "$(BLUE)→ Creating tag v$(VERSION)...$(RESET)" + @git tag -a "v$(VERSION)" -m "Release v$(VERSION)" + @git push origin "v$(VERSION)" + @echo "$(GREEN)✓ Tag v$(VERSION) created and pushed$(RESET)" + +release: cd ## Prepare release (run full CD pipeline) + @echo "$(BOLD)$(GREEN)✓ Release preparation complete$(RESET)" + @echo "" + @echo "$(CYAN)Next steps:$(RESET)" + @echo " 1. Update CHANGELOG.md" + @echo " 2. Update version in composer.json" + @echo " 3. Commit changes" + @echo " 4. Run: make tag VERSION=X.Y.Z" + @echo " 5. Push to GitHub" + @echo " 6. Create GitHub release" + +# ============================================================================== +# STATS & METRICS +# ============================================================================== + +stats: ## Show project statistics + @echo "$(BOLD)$(CYAN)Project Statistics$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo "Total PHP files: $$(find $(SRC_DIR) -name '*.php' | wc -l)" + @echo "Total test files: $$(find $(TEST_DIR) -name '*.php' | wc -l)" + @echo "Lines of code: $$(find $(SRC_DIR) -name '*.php' -exec cat {} \; | wc -l)" + @echo "Lines of tests: $$(find $(TEST_DIR) -name '*.php' -exec cat {} \; | wc -l)" + @echo "" + @echo "$(BOLD)$(CYAN)Directory Sizes$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @du -sh $(SRC_DIR) 2>/dev/null || true + @du -sh $(TEST_DIR) 2>/dev/null || true + @du -sh vendor 2>/dev/null || true + +loc: ## Count lines of code + @echo "$(BLUE)→ Counting lines of code...$(RESET)" + @find $(SRC_DIR) -name '*.php' -exec wc -l {} \; | awk '{sum += $$1} END {print "Source: " sum " lines"}' + @find $(TEST_DIR) -name '*.php' -exec wc -l {} \; | awk '{sum += $$1} END {print "Tests: " sum " lines"}' diff --git a/.make/local/Makefile.qa.mk b/.make/local/Makefile.qa.mk new file mode 100644 index 0000000..85f809f --- /dev/null +++ b/.make/local/Makefile.qa.mk @@ -0,0 +1,125 @@ +# ============================================================================== +# KaririCode\DevKit - Quality Assurance Targets +# ============================================================================== + +.PHONY: test test-unit test-integration test-functional coverage coverage-text \ + mutation mutation-report analyse phpstan phpstan-baseline psalm \ + psalm-baseline psalm-taint cs-check format format-dry lint + +# ============================================================================== +# TESTING +# ============================================================================== + +test: ## Run PHPUnit tests + @echo "$(BLUE)→ Running tests...$(RESET)" + @$(PHPUNIT) --colors=always --testdox + @echo "$(GREEN)✓ Tests passed$(RESET)" + +test-unit: ## Run unit tests only + @echo "$(BLUE)→ Running unit tests...$(RESET)" + @$(PHPUNIT) --colors=always --testdox --testsuite=Unit + @echo "$(GREEN)✓ Unit tests passed$(RESET)" + +test-integration: ## Run integration tests only + @echo "$(BLUE)→ Running integration tests...$(RESET)" + @$(PHPUNIT) --colors=always --testdox --testsuite=Integration + @echo "$(GREEN)✓ Integration tests passed$(RESET)" + +test-functional: ## Run functional tests only + @echo "$(BLUE)→ Running functional tests...$(RESET)" + @$(PHPUNIT) --colors=always --testdox --testsuite=Functional + @echo "$(GREEN)✓ Functional tests passed$(RESET)" + +coverage: ## Generate code coverage report + @echo "$(BLUE)→ Generating code coverage report...$(RESET)" + @mkdir -p $(COVERAGE_DIR) + @XDEBUG_MODE=coverage $(PHPUNIT) --coverage-html $(COVERAGE_DIR)/html \ + --coverage-clover $(COVERAGE_DIR)/clover.xml \ + --coverage-text=$(COVERAGE_DIR)/coverage.txt + @echo "$(GREEN)✓ Coverage report generated: $(COVERAGE_DIR)/html/index.html$(RESET)" + +coverage-text: ## Show coverage in terminal + @echo "$(BLUE)→ Generating text coverage...$(RESET)" + @XDEBUG_MODE=coverage $(PHPUNIT) --coverage-text + +mutation: ## Run mutation testing + @echo "$(BLUE)→ Running mutation tests...$(RESET)" + @mkdir -p $(CACHE_DIR)/infection + @XDEBUG_MODE=coverage $(PHP) -d pcov.enabled=0 $(INFECTION) --threads=4 --min-msi=80 --min-covered-msi=90 --show-mutations + @echo "$(GREEN)✓ Mutation testing complete$(RESET)" + +mutation-report: ## Generate mutation testing report + @echo "$(BLUE)→ Generating mutation report...$(RESET)" + @XDEBUG_MODE=coverage $(PHP) -d pcov.enabled=0 $(INFECTION) --threads=4 --min-msi=80 --min-covered-msi=90 \ + --log-verbosity=all --show-mutations + @echo "$(GREEN)✓ Report generated: infection.html$(RESET)" + +# ============================================================================== +# STATIC ANALYSIS +# ============================================================================== + +analyse: ## Run all static analysis tools + @echo "$(BLUE)→ Running static analysis...$(RESET)" + @$(MAKE) phpstan + @$(MAKE) psalm + @$(MAKE) cs-check + @echo "$(GREEN)✓ All analysis passed$(RESET)" + +phpstan: ## Run PHPStan analysis + @echo "$(BLUE)→ Running PHPStan...$(RESET)" + @mkdir -p $(CACHE_DIR)/phpstan + @if [ -n "$$(find $(SRC_DIR) -name '*.php' 2>/dev/null)" ]; then \ + $(PHPSTAN) analyse $(SRC_DIR) --level=max --memory-limit=512M && \ + echo "$(GREEN)✓ PHPStan analysis passed$(RESET)"; \ + else \ + echo "$(YELLOW)⚠ No PHP files found in $(SRC_DIR), skipping PHPStan$(RESET)"; \ + fi + +phpstan-baseline: ## Generate PHPStan baseline + @echo "$(BLUE)→ Generating PHPStan baseline...$(RESET)" + @$(PHPSTAN) analyse $(SRC_DIR) --level=max --generate-baseline + @echo "$(GREEN)✓ Baseline generated: phpstan-baseline.neon$(RESET)" + +psalm: ## Run Psalm analysis + @echo "$(BLUE)→ Running Psalm...$(RESET)" + @$(PSALM) --show-info=true --stats --no-cache + @echo "$(GREEN)✓ Psalm analysis passed$(RESET)" + +psalm-baseline: ## Generate Psalm baseline + @echo "$(BLUE)→ Generating Psalm baseline...$(RESET)" + @$(PSALM) --set-baseline=psalm-baseline.xml + @echo "$(GREEN)✓ Baseline generated: psalm-baseline.xml$(RESET)" + +psalm-taint: ## Run Psalm taint analysis + @echo "$(BLUE)→ Running Psalm taint analysis...$(RESET)" + @$(PSALM) --taint-analysis + @echo "$(GREEN)✓ Taint analysis complete$(RESET)" + +# ============================================================================== +# CODE STYLE & FORMATTING +# ============================================================================== + +cs-check: ## Check coding standards (PHPCS) + @echo "$(BLUE)→ Checking coding standards...$(RESET)" + @$(PHPCS) --standard=phpcs.xml --colors $(SRC_DIR) $(TEST_DIR) + @echo "$(GREEN)✓ Coding standards check passed$(RESET)" + +cbf-fix: ## Fix coding standards (PHPCS) + @echo "$(BLUE)→ Fixing coding standards...$(RESET)" + @$(PHPCBF) --standard=phpcs.xml --colors $(SRC_DIR) $(TEST_DIR) + @echo "$(GREEN)✓ Coding standards fixed$(RESET)" + +format: ## Format code with PHP-CS-Fixer + @echo "$(BLUE)→ Formatting code...$(RESET)" + @$(PHP_CS_FIXER) fix --config=.php-cs-fixer.php --verbose --diff + @$(MAKE) cbf-fix + @echo "$(GREEN)✓ Code formatted$(RESET)" + +format-dry: ## Show formatting changes without applying + @echo "$(BLUE)→ Dry-run code formatting...$(RESET)" + @$(PHP_CS_FIXER) fix --config=.php-cs-fixer.php --verbose --diff --dry-run + +lint: ## Lint PHP files for syntax errors + @echo "$(BLUE)→ Linting PHP files...$(RESET)" + @find $(SRC_DIR) $(TEST_DIR) -name "*.php" -print0 | xargs -0 -n1 $(PHP) -l > /dev/null + @echo "$(GREEN)✓ All PHP files are valid$(RESET)" diff --git a/.make/local/Makefile.setup.mk b/.make/local/Makefile.setup.mk new file mode 100644 index 0000000..c759e71 --- /dev/null +++ b/.make/local/Makefile.setup.mk @@ -0,0 +1,109 @@ +# ============================================================================== +# KaririCode\DevKit - Setup, Install & Clean Targets +# ============================================================================== + +.PHONY: check-php install install-dev fresh-install update verify-install \ + clean clean-all validate security security-strict outdated + +# ============================================================================== +# SETUP & INSTALLATION +# ============================================================================== + +check-php: ## Check PHP version requirement + $(call check_php_version) + +install: check-php ## Install dependencies + @echo "$(BLUE)→ Installing Composer dependencies...$(RESET)" + @if ! $(COMPOSER) validate --no-check-publish 2>/dev/null; then \ + echo "$(YELLOW)⚠ composer.lock outdated, updating dependencies...$(RESET)"; \ + $(COMPOSER) update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader; \ + else \ + $(COMPOSER) install --no-interaction --prefer-dist --optimize-autoloader; \ + fi + @$(MAKE) verify-install + @echo "$(GREEN)✓ Installation complete$(RESET)" + +install-dev: check-php ## Install dependencies with dev tools + @echo "$(BLUE)→ Installing Composer dependencies (dev mode)...$(RESET)" + @$(COMPOSER) install --no-interaction --prefer-dist + @$(MAKE) verify-install + @echo "$(GREEN)✓ Development installation complete$(RESET)" + +fresh-install: check-php ## Fresh install (removes lock file) + @echo "$(BLUE)→ Removing composer.lock...$(RESET)" + @rm -f composer.lock + @echo "$(BLUE)→ Installing fresh dependencies...$(RESET)" + @$(COMPOSER) install --no-interaction --prefer-dist --optimize-autoloader + @$(MAKE) verify-install + @echo "$(GREEN)✓ Fresh installation complete$(RESET)" + +update: ## Update dependencies + @echo "$(BLUE)→ Updating Composer dependencies...$(RESET)" + @$(COMPOSER) update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader + @echo "$(GREEN)✓ Dependencies updated$(RESET)" + +verify-install: ## Verify installation + @echo "$(BLUE)→ Verifying installation...$(RESET)" + $(call check_file,vendor/autoload.php,Autoloader) + $(call check_file,$(PHPUNIT),PHPUnit) + $(call check_file,$(PHPSTAN),PHPStan) + @echo "$(GREEN)✓ Installation verified$(RESET)" + +# ============================================================================== +# CLEANUP +# ============================================================================== + +clean: ## Clean build artifacts and caches + @echo "$(BLUE)→ Cleaning build artifacts...$(RESET)" + @rm -rf $(BUILD_DIR) + @rm -rf $(COVERAGE_DIR) + @rm -rf $(REPORTS_DIR) + @rm -rf $(CACHE_DIR) + @rm -rf .phpunit.cache + @rm -rf .phpunit.result.cache + @rm -rf .php-cs-fixer.cache + @rm -f infection.log infection.html + @echo "$(GREEN)✓ Clean complete$(RESET)" + +clean-all: clean ## Clean everything including vendor + @echo "$(BLUE)→ Removing vendor directory...$(RESET)" + @rm -rf vendor + @rm -f composer.lock + @echo "$(GREEN)✓ Deep clean complete$(RESET)" + +# ============================================================================== +# VALIDATION & SECURITY +# ============================================================================== + +validate: ## Validate composer.json + @echo "$(BLUE)→ Validating composer.json...$(RESET)" + @if [ ! -f "$(COMPOSER_BIN)" ]; then \ + echo "$(RED)✗ Composer not found. Please install Composer first.$(RESET)"; \ + echo "$(YELLOW) Visit: https://getcomposer.org/download/$(RESET)"; \ + exit 1; \ + fi + @$(COMPOSER) validate --strict --no-check-publish + @echo "$(GREEN)✓ composer.json is valid$(RESET)" + +security: ## Check for security vulnerabilities + @echo "$(BLUE)→ Checking for security vulnerabilities...$(RESET)" + @if $(COMPOSER) audit --no-dev --locked 2>&1 | grep -q "security vulnerability"; then \ + echo "$(RED)✗ Security vulnerabilities found$(RESET)"; \ + $(COMPOSER) audit --no-dev --locked; \ + exit 1; \ + elif $(COMPOSER) audit --no-dev --locked 2>&1 | grep -q "abandoned"; then \ + echo "$(YELLOW)⚠ Found abandoned packages (informational only):$(RESET)"; \ + $(COMPOSER) audit --no-dev --locked || true; \ + echo "$(GREEN)✓ No security vulnerabilities found$(RESET)"; \ + else \ + echo "$(GREEN)✓ No security vulnerabilities found$(RESET)"; \ + fi + +security-strict: ## Check for security vulnerabilities (strict mode) + @echo "$(BLUE)→ Checking for security vulnerabilities (strict)...$(RESET)" + @$(COMPOSER) audit + @echo "$(GREEN)✓ No security vulnerabilities or abandoned packages$(RESET)" + +outdated: ## Check for outdated dependencies + @echo "$(BLUE)→ Checking for outdated dependencies...$(RESET)" + @$(COMPOSER) outdated --direct diff --git a/.make/pipeline/Makefile.orchestration.mk b/.make/pipeline/Makefile.orchestration.mk new file mode 100644 index 0000000..e88c7a4 --- /dev/null +++ b/.make/pipeline/Makefile.orchestration.mk @@ -0,0 +1,53 @@ +# ============================================================================== +# KaririCode\DevKit - CI/CD Orchestration +# ============================================================================== +# Orquestra pipelines de CI/CD com composição de targets +# ============================================================================== + +.PHONY: check ci ci-full cd pre-commit + +# --- Quality Checks --- +check: lint analyse test ## Run all quality checks + @echo "$(GREEN)✓ All quality checks passed$(RESET)" + +# --- CI Pipeline --- +ci: ## Run CI pipeline (fast checks) + $(call pipeline_header,"KaririCode\\DevKit CI Pipeline") + @$(MAKE) --no-print-directory check-php + @$(MAKE) --no-print-directory lint + @$(MAKE) --no-print-directory cs-check + @$(MAKE) --no-print-directory phpstan + @$(MAKE) --no-print-directory psalm + @$(MAKE) --no-print-directory test + @echo "$(BOLD)$(GREEN)✓ CI pipeline completed successfully$(RESET)" + +# --- Full CI Pipeline --- +ci-full: ## Run full CI pipeline (with coverage) + $(call pipeline_header,"KaririCode\\DevKit Full CI Pipeline") + @$(MAKE) --no-print-directory check-php + @$(MAKE) --no-print-directory validate + @$(MAKE) --no-print-directory security + @$(MAKE) --no-print-directory lint + @$(MAKE) --no-print-directory cs-check + @$(MAKE) --no-print-directory phpstan + @$(MAKE) --no-print-directory psalm + @$(MAKE) --no-print-directory test + @$(MAKE) --no-print-directory coverage + @$(MAKE) --no-print-directory mutation + @echo "$(BOLD)$(GREEN)✓ Full CI pipeline completed successfully$(RESET)" + +# --- CD Pipeline --- +cd: ## Run CD pipeline (release preparation) + $(call pipeline_header,"KaririCode\\DevKit CD Pipeline") + @$(MAKE) --no-print-directory ci-full + @$(MAKE) --no-print-directory bench + @echo "$(BOLD)$(GREEN)✓ CD pipeline completed - Ready for release$(RESET)" + +# --- Pre-commit Hook --- +pre-commit: ## Run pre-commit checks + @echo "$(BLUE)→ Running pre-commit checks...$(RESET)" + @$(MAKE) --no-print-directory format + @$(MAKE) --no-print-directory lint + @$(MAKE) --no-print-directory analyse + @$(MAKE) --no-print-directory test-unit + @echo "$(GREEN)✓ Pre-commit checks passed$(RESET)" diff --git a/Makefile b/Makefile index fed8de1..31c28c1 100644 --- a/Makefile +++ b/Makefile @@ -1,324 +1,90 @@ -# ============================================================================ -# KaririCode DevKit - Professional Development Environment -# ============================================================================ -# Repository: https://github.com/KaririCode-Framework/kariricode-devkit -# License: MIT -# ============================================================================ +# ============================================================================== +# KaririCode\DevKit - Professional Development Makefile +# ============================================================================== +# +# Makefile modular seguindo SOLID principles e DRY +# Organização semântica por responsabilidade +# +# Usage: +# make +# make help - Display all available targets +# +# Author: Walmir Silva +# URL: https:\/\/github.com/KaririCode-Framework/kariricode-devkit +# ============================================================================== .DEFAULT_GOAL := help .PHONY: help -# Colors for output -BLUE := \033[0;34m -GREEN := \033[0;32m -YELLOW := \033[0;33m -RED := \033[0;31m -NC := \033[0m # No Color - -# Docker Compose command -DOCKER_COMPOSE := docker-compose -EXEC_PHP := $(DOCKER_COMPOSE) exec php -EXEC_PHP_ROOT := $(DOCKER_COMPOSE) exec -u root php - -# ============================================================================ -# HELP -# ============================================================================ +# ============================================================================== +# CORE INCLUDES (ordem de dependência) +# ============================================================================== + +MAKE_DIR := .make + +# 1. Core - Variáveis e funções compartilhadas +-include $(MAKE_DIR)/core/Makefile.variables.mk +-include $(MAKE_DIR)/core/Makefile.functions.mk + +# 2. Local - Targets locais +-include $(MAKE_DIR)/local/Makefile.setup.mk +-include $(MAKE_DIR)/local/Makefile.qa.mk +-include $(MAKE_DIR)/local/Makefile.helpers.mk + +# 3. Pipeline - Orquestração +-include $(MAKE_DIR)/pipeline/Makefile.orchestration.mk + +# 4. Docker - Targets Docker +-include $(MAKE_DIR)/docker/Makefile.docker-core.mk +-include $(MAKE_DIR)/docker/Makefile.docker-compose.mk +-include $(MAKE_DIR)/docker/Makefile.docker-qa.mk +-include $(MAKE_DIR)/docker/Makefile.docker-image.mk +-include $(MAKE_DIR)/docker/Makefile.docker-tools.mk + +# ============================================================================== +# HELP SYSTEM +# ============================================================================== + +define AWK_HELP_SCRIPT +BEGIN { \ + FS = ":.*?## "; \ + header_printed = 0; \ +} \ +/^[a-zA-Z0-9_-]+:.*?## / { \ + if (header_printed == 0) { \ + printf "\n$(BOLD)%s$(RESET)\n", TITLE; \ + header_printed = 1; \ + } \ + printf " $(CYAN)%-20s$(RESET) %s\n", $$1, $$2; \ +} +endef + +help: ## Display this help message + @echo "" + @echo "$(BOLD)$(CYAN)KaririCode\\DevKit - Development Makefile$(RESET)" + @echo "$(BLUE)═══════════════════════════════════════════════════════$(RESET)" + + @awk -v TITLE="🚀 Main Pipeline" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/pipeline/Makefile.orchestration.mk + @awk -v TITLE="🛠️ Setup & Maintenance" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/local/Makefile.setup.mk + @awk -v TITLE="🧪 Quality Assurance (Local)" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/local/Makefile.qa.mk + @awk -v TITLE="🧰 Developer Helpers" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/local/Makefile.helpers.mk + @awk -v TITLE="🐳 Docker QA Pipeline" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-qa.mk + @awk -v TITLE="🐳 Docker Compose" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-compose.mk + @awk -v TITLE="🐳 Docker Core" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-core.mk + @awk -v TITLE="🐳 Docker Tools" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-tools.mk + @awk -v TITLE="🐳 Docker Image" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-image.mk -help: ## Show this help message - @echo "$(BLUE)╔════════════════════════════════════════════════════════════════╗$(NC)" - @echo "$(BLUE)║ KaririCode DevKit - Available Commands ║$(NC)" - @echo "$(BLUE)╚════════════════════════════════════════════════════════════════╝$(NC)" @echo "" - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + @echo "$(BOLD)Usage Examples:$(RESET)" + @echo " make $(CYAN)install$(RESET) # Install all dependencies" + @echo " make $(CYAN)ci$(RESET) # Run local CI pipeline" + @echo " make $(CYAN)docker-ci$(RESET) # Run CI in Docker (isolated)" + @echo " make $(CYAN)docker-shell$(RESET) # Open interactive Docker shell" @echo "" -# ============================================================================ -# DOCKER MANAGEMENT -# ============================================================================: ## Create required directory structure -up: ## Start all containers in detached mode - @echo "$(BLUE)🚀 Starting containers...$(NC)" - @$(DOCKER_COMPOSE) up -d - @echo "$(GREEN)✓ Containers started successfully$(NC)" - -down: ## Stop and remove all containers - @echo "$(YELLOW)🛑 Stopping containers...$(NC)" - @$(DOCKER_COMPOSE) down - @echo "$(GREEN)✓ Containers stopped$(NC)" - -restart: down up ## Restart all containers - -status: ## Show status of all containers - @$(DOCKER_COMPOSE) ps - -logs: ## Show logs from all containers (use CTRL+C to exit) - @$(DOCKER_COMPOSE) logs -f - -logs-php: ## Show PHP container logs - @$(DOCKER_COMPOSE) logs -f php - -logs-redis: ## Show Redis container logs - @$(DOCKER_COMPOSE) logs -f redis - -shell: ## Access PHP container shell as app user - @$(EXEC_PHP) /bin/bash - -shell-root: ## Access PHP container shell as root user - @$(EXEC_PHP_ROOT) /bin/bash - -# ============================================================================ -# DEPENDENCY MANAGEMENT -# ============================================================================ - -install: ## Install composer dependencies - @echo "$(BLUE)📦 Installing dependencies...$(NC)" - @if [ -f composer.json ]; then \ - $(EXEC_PHP) composer install --no-interaction --prefer-dist --optimize-autoloader; \ - echo "$(GREEN)✓ Dependencies installed$(NC)"; \ - else \ - echo "$(YELLOW)⚠ No composer.json found$(NC)"; \ - echo "$(YELLOW) Run './install.sh' to create a new component$(NC)"; \ - fi - -update: ## Update composer dependencies - @echo "$(BLUE)🔄 Updating dependencies...$(NC)" - @$(EXEC_PHP) composer update - @echo "$(GREEN)✓ Dependencies updated$(NC)" - -require: ## Install a new package (use: make require PKG=vendor/package) - @test -n "$(PKG)" || (echo "$(RED)Error: PKG variable is required. Usage: make require PKG=vendor/package$(NC)" && exit 1) - @$(EXEC_PHP) composer require $(PKG) - -require-dev: ## Install a new dev package (use: make require-dev PKG=vendor/package) - @test -n "$(PKG)" || (echo "$(RED)Error: PKG variable is required. Usage: make require-dev PKG=vendor/package$(NC)" && exit 1) - @$(EXEC_PHP) composer require --dev $(PKG) - -autoload: ## Dump composer autoload - @$(EXEC_PHP) composer dump-autoload - -validate: ## Validate composer.json - @$(EXEC_PHP) composer validate --strict - -outdated: ## Show outdated packages - @$(EXEC_PHP) composer outdated --direct - -# ============================================================================ -# TESTING -# ============================================================================ - -test: ## Run all tests - @echo "$(BLUE)🧪 Running tests...$(NC)" - @$(EXEC_PHP) vendor/bin/phpunit --no-coverage --testdox - @echo "$(GREEN)✓ Tests completed$(NC)" - -test-coverage: ## Run tests with coverage report (HTML) - @echo "$(BLUE)🧪 Running tests with coverage...$(NC)" - @$(EXEC_PHP) vendor/bin/phpunit --coverage-html=coverage - @echo "$(GREEN)✓ Coverage report generated in ./coverage$(NC)" - -test-coverage-text: ## Run tests with coverage report (terminal) - @$(EXEC_PHP) vendor/bin/phpunit --coverage-text - -test-unit: ## Run unit tests only - @$(EXEC_PHP) vendor/bin/phpunit --testsuite=Unit - -test-integration: ## Run integration tests only - @$(EXEC_PHP) vendor/bin/phpunit --testsuite=Integration - -test-filter: ## Run specific test (use: make test-filter FILTER=TestClassName) - @test -n "$(FILTER)" || (echo "$(RED)Error: FILTER variable is required$(NC)" && exit 1) - @$(EXEC_PHP) vendor/bin/phpunit --filter $(FILTER) - -# ============================================================================ -# CODE QUALITY -# ============================================================================ - -cs-check: ## Check code style (dry-run) - @echo "$(BLUE)🔍 Checking code style...$(NC)" - @$(EXEC_PHP) vendor/bin/php-cs-fixer fix --dry-run --diff --verbose - -cs-fix: ## Fix code style - @echo "$(BLUE)✨ Fixing code style...$(NC)" - @$(EXEC_PHP) vendor/bin/php-cs-fixer fix - @echo "$(GREEN)✓ Code style fixed$(NC)" - -analyse: ## Run static analysis with PHPStan (max level) - @echo "$(BLUE)🔬 Running static analysis...$(NC)" - @$(EXEC_PHP) vendor/bin/phpstan analyse src tests --level=max - @echo "$(GREEN)✓ Static analysis completed$(NC)" - -analyse-baseline: ## Generate PHPStan baseline - @$(EXEC_PHP) vendor/bin/phpstan analyse src tests --level=max --generate-baseline - -phpmd: ## Run PHP Mess Detector - @echo "$(BLUE)🔍 Running PHP Mess Detector...$(NC)" - @$(EXEC_PHP) vendor/bin/phpmd src text devkit/.config/phpmd/ruleset.xml - -rector: ## Run Rector (dry-run) - @echo "$(BLUE)🔧 Running Rector...$(NC)" - @$(EXEC_PHP) vendor/bin/rector process --dry-run - -rector-fix: ## Run Rector and apply changes - @$(EXEC_PHP) vendor/bin/rector process - -# ============================================================================ -# COMPREHENSIVE QUALITY CHECKS -# ============================================================================ - -check: cs-check analyse phpmd ## Run all quality checks (CS, PHPStan, PHPMD) - @echo "$(GREEN)✓ All quality checks completed$(NC)" - -fix: cs-fix ## Fix all auto-fixable issues - -qa: fix test check ## Complete QA pipeline: fix, test, and check - -# ============================================================================ -# SECURITY -# ============================================================================ - -security: ## Check for security vulnerabilities - @echo "$(BLUE)🔒 Checking security vulnerabilities...$(NC)" - @$(EXEC_PHP) composer audit - @echo "$(GREEN)✓ Security check completed$(NC)" - -# ============================================================================ -# CACHE OPERATIONS -# ============================================================================ - -redis-cli: ## Access Redis CLI - @$(DOCKER_COMPOSE) exec redis redis-cli - -redis-flush: ## Flush Redis cache - @echo "$(YELLOW)🗑️ Flushing Redis cache...$(NC)" - @$(DOCKER_COMPOSE) exec redis redis-cli FLUSHALL - @echo "$(GREEN)✓ Redis cache flushed$(NC)" - -redis-info: ## Show Redis information - @$(DOCKER_COMPOSE) exec redis redis-cli INFO - -memcached-stats: ## Show Memcached statistics - @$(DOCKER_COMPOSE) exec memcached sh -c 'echo stats | nc localhost 11211' - -memcached-flush: ## Flush Memcached - @echo "$(YELLOW)🗑️ Flushing Memcached...$(NC)" - @$(DOCKER_COMPOSE) exec memcached sh -c 'echo flush_all | nc localhost 11211' - @echo "$(GREEN)✓ Memcached flushed$(NC)" - -# ============================================================================ -# UTILITIES -# ============================================================================ - -clean: ## Clean all generated files and caches - @echo "$(YELLOW)🧹 Cleaning generated files...$(NC)" - @sudo rm -rf vendor/ - @sudo rm -rf coverage/ - @sudo rm -rf .phpunit.cache/ - @sudo rm -rf .php-cs-fixer.cache - @sudo rm -rf composer.lock - @echo "$(GREEN)✓ Cleanup completed$(NC)" - -reset: clean down ## Complete reset (clean + remove containers) - @echo "$(GREEN)✓ Environment reset completed$(NC)" - -rebuild: reset force-rebuild install ## Rebuild environment from scratch - @echo "$(GREEN)✓ Environment rebuilt$(NC)" - -force-rebuild: ## Force rebuild images (no cache) and recreate containers - @echo "$(YELLOW)🔥 Forcing image rebuild (no cache)...$(NC)" - @$(DOCKER_COMPOSE) build --no-cache - @echo "$(YELLOW)🔥 Recreating containers...$(NC)" - @$(DOCKER_COMPOSE) up --force-recreate -d - @echo "$(GREEN)✓ Rebuild and recreate complete$(NC)" - -permissions: ## Fix file permissions - @echo "$(BLUE)🔧 Fixing permissions...$(NC)" - @$(EXEC_PHP_ROOT) chown -R app:app /var/www/html - @echo "$(GREEN)✓ Permissions fixed$(NC)" - -# ============================================================================ -# XDEBUG -# ============================================================================ - -xdebug-on: ## Enable Xdebug - @echo "$(BLUE)🐛 Enabling Xdebug...$(NC)" - @echo "XDEBUG_MODE=debug,coverage" > .env.xdebug - @$(DOCKER_COMPOSE) restart php - @echo "$(GREEN)✓ Xdebug enabled$(NC)" - -xdebug-off: ## Disable Xdebug - @echo "$(BLUE)🐛 Disabling Xdebug...$(NC)" - @echo "XDEBUG_MODE=off" > .env.xdebug - @$(DOCKER_COMPOSE) restart php - @echo "$(GREEN)✓ Xdebug disabled$(NC)" - -xdebug-status: ## Show Xdebug status - @$(EXEC_PHP) php -v | grep -i xdebug || echo "$(YELLOW)Xdebug is disabled$(NC)" - -# ============================================================================ -# INFORMATION -# ============================================================================ - -php-version: ## Show PHP version - @$(EXEC_PHP) php -v - -php-info: ## Show PHP information - @$(EXEC_PHP) php -i - -php-extensions: ## List installed PHP extensions - @$(EXEC_PHP) php -m - -composer-version: ## Show Composer version - @$(EXEC_PHP) composer --version - -env: ## Show environment variables - @$(EXEC_PHP) printenv - -# ============================================================================ -# DOCUMENTATION -# ============================================================================ - -docs: ## Generate API documentation (requires phpDocumentor) - @echo "$(BLUE)📚 Generating documentation...$(NC)" - @$(EXEC_PHP) vendor/bin/phpdoc - @echo "$(GREEN)✓ Documentation generated in ./docs$(NC)" - -# ============================================================================ -# CI/CD SIMULATION -# ============================================================================ - -ci: install test check security ## Simulate CI pipeline - @echo "$(GREEN)✓ CI pipeline completed successfully$(NC)" - -# ============================================================================ -# ADVANCED OPERATIONS -# ============================================================================ - -profile: ## Profile application performance - @$(EXEC_PHP) vendor/bin/phpbench run --report=default - -benchmark: ## Run benchmarks - @$(EXEC_PHP) vendor/bin/phpbench run - -metrics: ## Generate code metrics - @$(EXEC_PHP) vendor/bin/phpmetrics --report-html=metrics src - -# ============================================================================ -# DEVELOPMENT HELPERS -# ============================================================================ - -watch-tests: ## Watch and run tests on file changes (requires watchman or inotifywait) - @echo "$(BLUE)👀 Watching for changes...$(NC)" - @while true; do \ - inotifywait -r -e modify src/ tests/ 2>/dev/null && make test; \ - done - -init: ## Initialize new component development environment - @echo "$(BLUE)🎬 Initializing development environment...$(NC)" - @$(MAKE) up - @$(MAKE) install - @echo "$(GREEN)✓ Development environment ready!$(NC)" - @echo "" - @echo "$(YELLOW)Next steps:$(NC)" - @echo " 1. Edit your code in ./src" - @echo " 2. Run $(GREEN)make test$(NC) to execute tests" - @echo " 3. Run $(GREEN)make qa$(NC) for complete quality assurance" - @echo "" \ No newline at end of file +debug-composer: ## Debug composer configuration + @echo "COMPOSER_BIN = '$(COMPOSER_BIN)'" + @echo "COMPOSER = '$(COMPOSER)'" + @command -v composer || echo "Composer not found with command -v" + @which composer || echo "Composer not found with which" + @type composer || echo "Composer not found with type" From bf603165bdce570860192722cb7b9407ab51c282 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Mon, 3 Nov 2025 20:07:03 -0300 Subject: [PATCH 6/7] feat(docker): implement advanced port management and health check system - Add automatic port conflict detection with lsof and ss - Implement fix-ports command for automatic resolution - Add diagnose-ports for detailed conflict analysis - Add kill-port and port-scan utilities - Improve Memcached health check (nc instead of bash) - Update docker-compose.yml with env_file support - Remove user constraints from PHP container - Add comprehensive Redis configuration variables - Update .env.example with session handler and Redis config - Enhance Makefile targets for Docker management - Add clean-docker and check-docker-ports commands - Update documentation with troubleshooting guides - Remove deprecated install.sh script Breaking changes: - Removed install.sh (replaced by make install-dev) - Port conflict detection now fails fast with actionable messages - DEMO_MODE and HEALTH_CHECK_INSTALL disabled by default Fixes: - PHP container restart loop due to permission issues - Redis configuration errors causing crashes - Session handler conflicts - Volume mount read-only conflicts Closes #issue-number --- .docs/MAKEFILE-compose.md | 781 +++++++++++++++++++++--- .env.example | 250 ++++++-- .make/core/Makefile.functions.mk | 26 +- .make/docker/Makefile.docker-compose.mk | 369 ++++++++--- .make/local/Makefile.helpers.mk | 154 ++--- .make/local/Makefile.setup.mk | 56 +- Makefile | 16 +- composer.json | 14 - docker-compose.yml | 429 ++++++++++++- install.sh | 638 ------------------- 10 files changed, 1738 insertions(+), 995 deletions(-) delete mode 100644 install.sh diff --git a/.docs/MAKEFILE-compose.md b/.docs/MAKEFILE-compose.md index d782c68..4bf200f 100644 --- a/.docs/MAKEFILE-compose.md +++ b/.docs/MAKEFILE-compose.md @@ -20,13 +20,14 @@ Part of the [KaririCode Framework](https://kariricode.org) ecosystem 1. [Overview](#overview) 2. [Environment Setup](#environment-setup) -3. [Lifecycle Management](#lifecycle-management) -4. [Service Monitoring](#service-monitoring) -5. [Container Interaction](#container-interaction) -6. [Configuration Management](#configuration-management) -7. [Troubleshooting](#troubleshooting) -8. [Advanced Workflows](#advanced-workflows) -9. [Production Considerations](#production-considerations) +3. [Port Conflict Detection & Resolution](#port-conflict-detection--resolution) +4. [Lifecycle Management](#lifecycle-management) +5. [Service Monitoring](#service-monitoring) +6. [Container Interaction](#container-interaction) +7. [Configuration Management](#configuration-management) +8. [Troubleshooting](#troubleshooting) +9. [Advanced Workflows](#advanced-workflows) +10. [Production Considerations](#production-considerations) --- @@ -34,22 +35,22 @@ Part of the [KaririCode Framework](https://kariricode.org) ecosystem ### Architecture ``` -┌─────────────────────────────────────────────┐ -│ Docker Compose Stack │ -├─────────────────────────────────────────────┤ -│ │ -│ ┌─────────────┐ ┌──────────────┐ │ -│ │ PHP-FPM + │◄────►│ Memcached │ │ -│ │ Nginx + │ │ (11211) │ │ -│ │ Redis │ └──────────────┘ │ -│ │ (80, 6379) │ │ -│ └─────────────┘ │ -│ │ │ -│ │ Volume Mount │ -│ ▼ │ -│ /var/www/html ◄────► ./ │ -│ │ -└─────────────────────────────────────────────┘ +┌───────────────────────────────────────────┐ +│ Docker Compose Stack │ +├───────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌──────────────┐ │ +│ │ PHP-FPM + │◄────►│ Memcached │ │ +│ │ Nginx + │ │ (11211) │ │ +│ │ Redis │ └──────────────┘ │ +│ │ (80, 6379) │ │ +│ └─────────────┘ │ +│ │ │ +│ │ Volume Mount │ +│ ▼ │ +│ /var/www/html ◄────► ./ │ +│ │ +└───────────────────────────────────────────┘ │ │ Bridge Network │ kariricode_network @@ -74,7 +75,8 @@ Part of the [KaririCode Framework](https://kariricode.org) ecosystem ✅ **Health Monitoring**: Built-in health checks ✅ **Xdebug Support**: Configurable debugging ✅ **Auto-restart**: Service recovery on failure -✅ **Environment Variables**: Flexible configuration via `.env` +✅ **Environment Variables**: Flexible configuration via `.env` +✅ **Port Conflict Detection**: Automatic detection and resolution --- @@ -107,8 +109,8 @@ DEMO_MODE=false HEALTH_CHECK_INSTALL=true # Cache Services -REDIS_PORT=63777 # Host port → container:6379 -MEMCACHED_PORT=11210 # Host port → container:11211 +REDIS_PORT=6379 # Host port → container:6379 +MEMCACHED_PORT=11211 # Host port → container:11211 # Xdebug XDEBUG_MODE=off # off/debug/coverage/profile @@ -128,37 +130,611 @@ make env-check # ✓ .env file exists # APP_NAME=kariricode-devkit # APP_PORT=8089 -# REDIS_PORT=63777 +# REDIS_PORT=6379 # ... # ╚════════════════════════════════════════════════════════╝ ``` -### Port Conflict Resolution +--- + +## Port Conflict Detection & Resolution + +### Overview + +**The Problem**: Port conflicts prevent Docker containers from starting, causing cryptic error messages like: +``` +Error: failed to bind host port for 0.0.0.0:11211:172.20.0.2:11211/tcp: address already in use +``` + +**The Solution**: Automated detection and resolution tools that identify conflicting processes and free up ports. + +--- + +### Quick Start + +#### Safe Startup (Recommended) +```bash +# Automatically checks ports before starting +make up-safe + +# Workflow: +# 1. Cleans orphaned Docker containers +# 2. Checks for Docker port conflicts +# 3. Checks for system port conflicts +# 4. Starts services if all clear +# 5. Shows status + +# If conflicts detected: +# ✗ Port 6379 in use by system process PID 1234 (redis-server) +# +# Resolution options: +# 1. Run: make diagnose-ports for detailed info +# 2. Run: make fix-ports to auto-resolve +# 3. Run: make kill-port PORT=6379 for specific port +``` + +--- + +### Port Conflict Commands + +#### 1. Check Ports +```bash +# Quick check for conflicts +make check-ports + +# What it does: +# 1. Cleans orphaned Docker containers +# 2. Checks Docker containers for port usage +# 3. Checks system processes for port usage +# 4. Reports status + +# Success output: +# → Checking system ports for conflicts... +# ✓ Port 8089 is available +# ✓ Port 6379 is available +# ✓ Port 11211 is available +# ✓ All ports available + +# Failure output: +# ✗ Port 6379 in use by system process PID 1234 (redis-server) +# +# Resolution options: +# 1. Run: make diagnose-ports for detailed info +# 2. Run: make fix-ports to auto-resolve +# 3. Run: make kill-port PORT=6379 for specific port +``` + +#### 2. Diagnose Ports +```bash +# Detailed port conflict analysis +make diagnose-ports + +# Output example: +# Port Conflict Diagnosis +# ╔════════════════════════════════════════════════════════╗ +# +# Required Ports: +# APP_PORT: 8089 +# REDIS_PORT: 6379 +# MEMCACHED_PORT: 11211 +# +# System Port Status: +# +# Port 8089: +# Status: AVAILABLE +# +# Port 6379: +# PID: 1234 | Command: redis-server | User: redis +# Status: IN USE (lsof) +# To kill: make kill-port PORT=6379 +# Or stop service: sudo systemctl stop redis +# +# Port 11211: +# PID: 5678 | Command: memcached | User: memcache +# Status: IN USE (ss) +# To kill: make kill-port PORT=11211 +# Or stop service: sudo systemctl stop memcached +# +# Docker Containers (All): +# None found +# +# ╚════════════════════════════════════════════════════════╝ +# +# Suggested Actions: +# 1. make fix-ports - Auto-kill conflicting processes +# 2. make kill-port PORT= - Kill specific process +# 3. sudo systemctl stop redis - Stop Redis service +# 4. sudo systemctl stop memcached - Stop Memcached service +# 5. Edit .env to use different ports: +# REDIS_PORT=6380 +# MEMCACHED_PORT=11212 +``` + +#### 3. Fix Ports (Automatic Resolution) +```bash +# Interactive automatic fix +make fix-ports + +# Workflow: +# ⚠ This will attempt to free up conflicting ports +# Docker containers and system processes will be terminated +# +# Continue? [y/N] y +# +# → Cleaning orphaned Docker resources... +# ✓ Docker cleanup complete +# +# → Scanning and fixing system port conflicts... +# → Terminating redis-server (PID 1234) on port 6379... +# ✓ Port 6379 freed +# → Terminating memcached (PID 5678) on port 11211... +# ✓ Port 11211 freed +# +# → Checking system ports for conflicts... +# ✓ Port 8089 is available +# ✓ Port 6379 is available +# ✓ Port 11211 is available +# ✓ All ports available +``` + +**What `fix-ports` does:** +1. Cleans orphaned Docker containers +2. Identifies processes using required ports +3. Attempts graceful shutdown (SIGTERM) +4. Forces shutdown if needed (SIGKILL) +5. Verifies ports are freed +6. Re-checks all ports + +**Safety Features:** +- ✅ Interactive confirmation required +- ✅ Graceful shutdown first (15 seconds wait) +- ✅ Force only if graceful fails +- ✅ Verifies success after each action +- ✅ Final check confirms all ports clear + +#### 4. Kill Specific Port +```bash +# Kill process on specific port +make kill-port PORT=6379 + +# Workflow: +# → Checking port 6379... +# Found process: PID 1234 (redis-server) +# Attempting graceful shutdown... +# ✓ Port 6379 is now free + +# For stubborn processes: +# Process didn't stop, forcing shutdown... +# ✓ Port 6379 is now free +``` + +**Use Cases:** +- Kill specific conflicting process +- Don't want to touch other ports +- Selective cleanup + +#### 5. Port Scanner +```bash +# Scan common ports for conflicts +make port-scan + +# Output: +# Port Scanner +# ╔════════════════════════════════════════════════════════╗ +# +# ✓ Port 80: Available +# ✗ Port 443: IN USE (PID 890 - nginx) +# ✓ Port 3000: Available +# ✗ Port 3306: IN USE (PID 1122 - mysqld) +# ✓ Port 5432: Available +# ✗ Port 6379: IN USE (PID 1234 - redis-server) +# ✓ Port 8000: Available +# ✓ Port 8080: Available +# ✓ Port 8089: Available +# ✓ Port 9000: Available +# ✗ Port 11211: IN USE (PID 5678 - memcached) +# ✓ Port 27017: Available +# +# ╚════════════════════════════════════════════════════════╝ +``` + +**Use Cases:** +- Quick overview of port availability +- Planning port assignments +- Identifying system services +- Troubleshooting network issues + +#### 6. Clean Docker Resources +```bash +# Remove orphaned containers and networks +make clean-docker + +# Output: +# → Cleaning orphaned Docker resources... +# Removing stopped containers... +# Deleted Containers: +# a9d5c257ff5f +# 34704960e194 +# +# Removing unused networks... +# Deleted Networks: +# kariricode-devkit_network +# +# ✓ Docker cleanup complete +``` + +**When to use:** +- Before starting services +- After failed `docker compose up` +- When seeing "address already in use" errors +- Regular maintenance + +--- + +### Port Conflict Workflows + +#### Workflow 1: First Time Setup +```bash +# 1. Clone repository +git clone https://github.com/KaririCode-Framework/kariricode-devkit.git +cd kariricode-devkit + +# 2. Create .env +cp .env.example .env + +# 3. Check for conflicts +make check-ports + +# If conflicts found: +# 4. View detailed info +make diagnose-ports + +# 5. Resolve conflicts +make fix-ports + +# 6. Start services safely +make up-safe +``` + +#### Workflow 2: Port Already in Use Error +```bash +# Scenario: You tried 'make up' and got: +# Error: address already in use + +# 1. Diagnose the problem +make diagnose-ports -**Problem:** Port already in use +# 2. Option A: Kill conflicting processes +make fix-ports + +# 3. Option B: Change ports in .env +nano .env +# Change: REDIS_PORT=6380 +# MEMCACHED_PORT=11212 + +# 4. Retry startup +make up-safe +``` + +#### Workflow 3: System Service Conflicts ```bash -# 1. Identify conflicting process -sudo lsof -i :8089 -# COMMAND PID USER -# nginx 1234 root +# Scenario: Local Redis/Memcached running + +# 1. Identify services +make diagnose-ports +# → Redis (6379): PID 1234 +# → Memcached (11211): PID 5678 -# 2. Option A: Stop conflicting service -sudo systemctl stop nginx +# 2. Option A: Stop system services +sudo systemctl stop redis +sudo systemctl stop memcached -# 3. Option B: Change port in .env -echo "APP_PORT=8090" >> .env +# 3. Option B: Disable autostart +sudo systemctl disable redis +sudo systemctl disable memcached -# 4. Restart -make down && make up +# 4. Option C: Use different ports +echo "REDIS_PORT=6380" >> .env +echo "MEMCACHED_PORT=11212" >> .env + +# 5. Start Docker services +make up-safe ``` -**Common Port Conflicts:** +#### Workflow 4: Orphaned Docker Containers +```bash +# Scenario: Previous containers not cleaned up -| Port | Service | Solution | -|------|---------|----------| -| 8089 | Nginx/Apache | Change `APP_PORT` | -| 6379 | Redis | Change `REDIS_PORT` | -| 11211 | Memcached | Change `MEMCACHED_PORT` | +# 1. Clean Docker resources +make clean-docker + +# 2. Verify cleanup +docker ps -a +# Should show no kariricode-devkit containers + +# 3. Check ports again +make check-ports + +# 4. Start fresh +make up +``` + +#### Workflow 5: Multiple Developers +```bash +# Scenario: Different developers, different ports + +# Developer A (.env) +APP_PORT=8089 +REDIS_PORT=6379 +MEMCACHED_PORT=11211 + +# Developer B (.env) - all different +APP_PORT=8090 +REDIS_PORT=6380 +MEMCACHED_PORT=11212 + +# Each developer: +make check-ports # Verify their ports are free +make up-safe # Start with their configuration +``` + +--- + +### Common Port Conflict Scenarios + +#### Scenario 1: Local Redis Running +**Problem:** +``` +✗ Port 6379 in use by system process PID 1234 (redis-server) +``` + +**Solutions:** + +**Option A: Stop system Redis** +```bash +# Temporary stop +sudo systemctl stop redis + +# Permanent disable +sudo systemctl disable redis + +# Start Docker services +make up-safe +``` + +**Option B: Use different port** +```bash +# Change Docker port +echo "REDIS_PORT=6380" >> .env + +# Restart +make up-safe + +# Access Redis on new port +redis-cli -h localhost -p 6380 +``` + +**Option C: Kill process** +```bash +make kill-port PORT=6379 +make up-safe +``` + +#### Scenario 2: Local Memcached Running +**Problem:** +``` +✗ Port 11211 in use by system process PID 5678 (memcached) +``` + +**Solutions:** + +**Option A: Stop system Memcached** +```bash +sudo systemctl stop memcached +make up-safe +``` + +**Option B: Change port** +```bash +echo "MEMCACHED_PORT=11212" >> .env +make up-safe +``` + +#### Scenario 3: Orphaned Docker Containers +**Problem:** +``` +Error: failed to bind host port for 0.0.0.0:11211 +✓ Port 11211 is available (via lsof) # Confusing! +``` + +**Cause:** Docker containers in "Created" or "Exited" state still hold port bindings + +**Solution:** +```bash +# Clean orphaned containers +make clean-docker + +# Verify cleanup +docker ps -a | grep kariricode +# Should be empty + +# Start fresh +make up +``` + +#### Scenario 4: Multiple Projects +**Problem:** Multiple projects using same ports + +**Solution 1: Port Namespacing** +```bash +# Project A +APP_PORT=8089 +REDIS_PORT=6379 +MEMCACHED_PORT=11211 + +# Project B +APP_PORT=8189 +REDIS_PORT=6479 +MEMCACHED_PORT=11311 + +# Project C +APP_PORT=8289 +REDIS_PORT=6579 +MEMCACHED_PORT=11411 +``` + +**Solution 2: Stop Other Projects** +```bash +# In other project directories +make down + +# In current project +make up-safe +``` + +--- + +### Troubleshooting Port Issues + +#### Issue: "Permission Denied" When Killing Process + +**Symptoms:** +``` +✗ Failed to free port 6379 (may require sudo) +``` + +**Solution:** +```bash +# Option A: Use sudo +sudo make kill-port PORT=6379 + +# Option B: Stop service properly +sudo systemctl stop redis + +# Option C: Change port +echo "REDIS_PORT=6380" >> .env +``` + +#### Issue: Port Shows Available But Still Fails + +**Diagnosis:** +```bash +# Check with multiple tools +lsof -i :6379 +ss -ltn | grep :6379 +netstat -tlnp | grep :6379 + +# Check Docker specifically +docker ps -a --format "{{.Names}}\t{{.Ports}}" | grep 6379 +``` + +**Solution:** +```bash +# Clean everything +make clean-docker +docker system prune -f + +# Verify +make check-ports + +# Start fresh +make up +``` + +#### Issue: Ports Freed But Container Won't Start + +**Diagnosis:** +```bash +# Check detailed error +make up +make logs + +# Common causes: +# - Image not found +# - Invalid .env syntax +# - Volume permission issues +``` + +**Solution:** +```bash +# Pull latest image +docker pull kariricode/php-api-stack:dev + +# Validate .env +make env-check + +# Fix permissions +sudo chown -R $USER:$USER . + +# Retry +make up-safe +``` + +--- + +### Best Practices + +#### 1. Always Use `up-safe` for First Start +```bash +# ✅ Good: Checks ports first +make up-safe + +# ❌ Avoid: May fail with cryptic errors +make up +``` + +#### 2. Regular Cleanup +```bash +# Weekly maintenance +make clean-docker +make check-ports + +# After failed starts +make clean-docker +make up-safe +``` + +#### 3. Port Reservation Strategy +```bash +# .env - Use port ranges per project +# Project A: 808X, 637X, 1121X +APP_PORT=8089 +REDIS_PORT=6379 +MEMCACHED_PORT=11211 + +# Project B: 818X, 647X, 1131X +APP_PORT=8189 +REDIS_PORT=6479 +MEMCACHED_PORT=11311 +``` + +#### 4. Document Custom Ports +```bash +# .env +# Custom ports to avoid conflict with local Redis +REDIS_PORT=6380 # Changed from 6379 +MEMCACHED_PORT=11212 # Changed from 11211 +``` + +#### 5. Automated Conflict Resolution in Scripts +```bash +#!/bin/bash +# start-dev.sh + +# Ensure ports are free +make check-ports || make fix-ports + +# Start services +make up-safe + +# Wait for healthy +until make health | grep -q "healthy"; do + echo "Waiting for services..." + sleep 2 +done + +echo "✓ Development environment ready" +``` --- @@ -183,10 +759,26 @@ make up # ╔════════════════════════════════════════════════════════╗ # NAME STATUS PORTS # kariricode-devkit_php Up 5 seconds 0.0.0.0:8089->80/tcp -# kariricode-devkit_memcached Up 5 seconds 0.0.0.0:11210->11211/tcp +# kariricode-devkit_memcached Up 5 seconds 0.0.0.0:11211->11211/tcp # ╚════════════════════════════════════════════════════════╝ ``` +#### Safe Start (Recommended) +```bash +# Start with port conflict detection +make up-safe + +# Executes: +# 1. make check-ports (cleans Docker, checks ports) +# 2. make up (starts services) + +# Use this for: +# - First time setup +# - After system reboot +# - When unsure about port conflicts +# - Team onboarding +``` + #### Start with Build ```bash # Force rebuild images before starting @@ -320,7 +912,7 @@ make ports # ╔════════════════════════════════════════════════════════╗ # NAME PORTS # *_php 0.0.0.0:8089->80/tcp, :::6379->6379/tcp -# *_memcached 0.0.0.0:11210->11211/tcp +# *_memcached 0.0.0.0:11211->11211/tcp # ╚════════════════════════════════════════════════════════╝ ``` @@ -330,10 +922,10 @@ make ports curl http://localhost:8089 # Redis (from host, if exposed) -redis-cli -h localhost -p 63777 +redis-cli -h localhost -p 6379 # Memcached (from host) -echo "stats" | nc localhost 11210 +echo "stats" | nc localhost 11211 ``` --- @@ -503,6 +1095,10 @@ make composer-update ## Troubleshooting +### Port-Related Issues + +See complete port troubleshooting in [Port Conflict Detection & Resolution](#port-conflict-detection--resolution) section. + ### Issue 1: Services Won't Start **Symptoms:** @@ -523,15 +1119,15 @@ make logs **Solutions:** ```bash # Port conflict -echo "APP_PORT=8090" >> .env -make down && make up +make diagnose-ports +make fix-ports # Invalid .env make env-check # Fix syntax errors # Missing image -make docker-pull +docker pull kariricode/php-api-stack:dev ``` ### Issue 2: Can't Connect to Services @@ -563,7 +1159,7 @@ curl http://localhost:$(grep APP_PORT .env | cut -d= -f2) # Network issue make down docker network prune -make up +make up-safe ``` ### Issue 3: Permission Denied @@ -643,7 +1239,7 @@ make exec-php CMD="php -m | grep xdebug" ### Development with Live Reload ```bash # 1. Start services -make up +make up-safe # 2. Open separate terminal for logs make logs-follow SERVICE=php @@ -663,7 +1259,7 @@ curl http://localhost:8089/health ```bash # Use .env.development cp .env.development .env -make up +make up-safe # Characteristics: # - XDEBUG_MODE=debug @@ -750,7 +1346,7 @@ docker cp kariricode-devkit_php:/tmp/xdebug/cachegrind.out.1234 ./ ### Integration Testing Workflow ```bash # 1. Start full stack -make up +make up-safe # 2. Wait for services sleep 3 @@ -858,8 +1454,11 @@ jobs: - name: Create .env run: cp .env.example .env + - name: Check for port conflicts + run: make check-ports + - name: Start Docker Compose - run: make up + run: make up-safe - name: Wait for services run: sleep 5 @@ -888,9 +1487,11 @@ integration-tests: DOCKER_DRIVER: overlay2 before_script: - - apk add --no-cache make + - apk add --no-cache make lsof - cp .env.example .env - - make up + - make clean-docker + - make check-ports + - make up-safe script: - make test-compose @@ -911,6 +1512,17 @@ integration-tests: ## Command Reference +### Port Management +```bash +make check-ports # Check for port conflicts +make diagnose-ports # Detailed port analysis +make fix-ports # Auto-fix port conflicts (interactive) +make kill-port PORT=X # Kill specific port +make port-scan # Scan common ports +make clean-docker # Clean orphaned containers +make up-safe # Safe startup with port check +``` + ### Lifecycle ```bash make up # Start services @@ -972,16 +1584,26 @@ ports: - "${APP_PORT:-8089}:80" ``` -### 2. Clean Up Regularly +### 2. Always Check Ports Before Starting +```bash +# ✅ Recommended workflow +make check-ports && make up + +# ✅ Or use safe startup +make up-safe +``` + +### 3. Clean Up Regularly ```bash # Weekly cleanup +make clean-docker make prune # Before major changes make rebuild ``` -### 3. Monitor Health +### 4. Monitor Health ```bash # Add to daily routine make health @@ -990,7 +1612,7 @@ make health make logs SERVICE= ``` -### 4. Version Control +### 5. Version Control ```bash # ✅ Commit - docker-compose.yml @@ -1003,14 +1625,12 @@ make logs SERVICE= - Build artifacts ``` -### 5. Document Custom Changes -```yaml -# docker-compose.yml -services: - php: - # Custom: Added for X feature (see issue #123) - environment: - CUSTOM_VAR: value +### 6. Document Port Changes +```bash +# .env +# Custom ports to avoid local Redis conflict +REDIS_PORT=6380 # Changed from 6379 (local Redis running) +MEMCACHED_PORT=11212 # Changed from 11211 (local Memcached) ``` --- @@ -1020,11 +1640,18 @@ services: ╔═══════════════════════════════════════════════════════════╗ ║ Docker Compose Quick Reference ║ ╠═══════════════════════════════════════════════════════════╣ +║ SAFE START │ make up-safe ║ ║ START │ make up ║ ║ STOP │ make down ║ ║ RESTART │ make restart ║ ║ REBUILD │ make rebuild ║ ║──────────────┼────────────────────────────────────────────║ +║ CHECK PORTS │ make check-ports ║ +║ DIAGNOSE │ make diagnose-ports ║ +║ FIX PORTS │ make fix-ports ║ +║ KILL PORT │ make kill-port PORT=6379 ║ +║ PORT SCAN │ make port-scan ║ +║──────────────┼────────────────────────────────────────────║ ║ STATUS │ make status ║ ║ LOGS │ make logs-follow ║ ║ HEALTH │ make health ║ @@ -1037,10 +1664,20 @@ services: ║ CHECK ENV │ make env-check ║ ║ INSPECT │ make inspect-php ║ ╚═══════════════════════════════════════════════════════════╝ + +Port Conflict Workflow: + 1. make diagnose-ports # Identify conflicts + 2. make fix-ports # Auto-resolve + 3. make up-safe # Start safely + +Or change ports: + echo "REDIS_PORT=6380" >> .env + echo "MEMCACHED_PORT=11212" >> .env + make up-safe ``` --- **Version**: 1.0.0 -**Module**: `Makefile.docker-compose.mk` -**Maintainer**: Walmir Silva +**Module**: `Makefile.docker-compose.mk` +**Maintainer**: Walmir Silva \ No newline at end of file diff --git a/.env.example b/.env.example index 80cb40e..681d2d9 100644 --- a/.env.example +++ b/.env.example @@ -1,54 +1,212 @@ -# ============================================================================ +# ============================================================================== # KaririCode DevKit - Environment Configuration -# ============================================================================ -# IMPORTANTE: Copie este arquivo para .env e ajuste conforme necessário -# cp .env.example .env -# ============================================================================ - -# ============================================================================ -# PROJECT CONFIGURATION -# ============================================================================ +# ============================================================================== +# Professional environment variables for Docker Compose +# Copy to .env and customize: cp .env.example .env +# ============================================================================== + +# ============================================================================== +# APPLICATION +# ============================================================================== APP_NAME=kariricode-devkit -APP_ENV=development -APP_DEBUG=true +APP_ENV=development # development|production|testing +APP_DEBUG=true # true|false +APP_SECRET=change-me-in-production # Generate: openssl rand -hex 32 +APP_VERSION=dev +SYMFONY_ENV=dev # Symfony environment + +# ============================================================================== +# DOCKER & SYSTEM +# ============================================================================== +# User/Group IDs (match host user for volume permissions) +# Note: PHP container runs as root internally but processes run as www-data +UID=1000 +GID=1000 + +# Timezone +TZ=UTC # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +# Volume consistency (macOS performance) +VOLUME_CONSISTENCY=cached # cached|delegated|consistent + +# ============================================================================== +# PORTS +# ============================================================================== APP_PORT=8089 +REDIS_PORT=6379 +MEMCACHED_PORT=11211 -# Health Check Installation (optional) -# Set to 'true' to install health check endpoint -# Automatically enabled when DEMO_MODE=true -DEMO_MODE=false -HEALTH_CHECK_INSTALL=true +# ============================================================================== +# PHP SERVICE (kariricode/php-api-stack) +# ============================================================================== +PHP_STACK_VERSION=dev # dev|latest|1.0.0 -# ============================================================================ -# PORTS - Ajuste se houver conflito com serviços existentes -# Usually 6379 for Redis and 11211 for Memcached -# ============================================================================ -REDIS_PORT=63777 -MEMCACHED_PORT=11210 +# PHP Configuration +PHP_MEMORY_LIMIT=2G +PHP_MAX_EXECUTION_TIME=300 +PHP_UPLOAD_MAX_FILESIZE=50M +PHP_POST_MAX_SIZE=50M +# Resource Limits +PHP_CPU_LIMIT=2.0 # CPU cores limit +PHP_CPU_RESERVATION=0.5 # CPU cores reservation +PHP_MEMORY_RESERVATION=512M # Memory reservation -# ============================================================================ -# XDEBUG - Descomente para habilitar -# ============================================================================ -XDEBUG_MODE=off +# OPcache +OPCACHE_ENABLE=1 +OPCACHE_VALIDATE_TIMESTAMPS=1 # 0 for production, 1 for development +OPCACHE_REVALIDATE_FREQ=2 # seconds + +# PHP-FPM Configuration +PHP_FPM_PM=dynamic # static|dynamic|ondemand +PHP_FPM_PM_MAX_CHILDREN=50 +PHP_FPM_PM_START_SERVERS=5 +PHP_FPM_PM_MIN_SPARE_SERVERS=5 +PHP_FPM_PM_MAX_SPARE_SERVERS=10 +PHP_FPM_PM_MAX_REQUESTS=500 + +# ============================================================================== +# XDEBUG +# ============================================================================== +XDEBUG_MODE=off # off|debug|coverage|profile XDEBUG_CLIENT_HOST=host.docker.internal -COMPOSER_MEMORY_LIMIT=-1 - -# ============================================================================ -# REDIS -# ============================================================================ -REDIS_MAXMEMORY=256mb -REDIS_MAXMEMORY_POLICY=allkeys-lru -REDIS_APPENDONLY=yes - -# ============================================================================ -# MEMCACHED -# ============================================================================ -MEMCACHED_MEMORY=256 - -# ============================================================================ -# RESOLUÇÃO DE CONFLITOS: -# 1. Identifique a porta em uso: sudo lsof -i :6379 -# 2. Altere REDIS_PORT para 6380 ou outra porta disponível -# 3. Execute: make down && make up -# ============================================================================ \ No newline at end of file +XDEBUG_CLIENT_PORT=9003 +XDEBUG_SESSION=PHPSTORM + +# ============================================================================== +# SESSION HANDLER +# ============================================================================== +# Session storage: files (local) or redis (distributed) +SESSION_SAVE_HANDLER=files # files|redis +SESSION_SAVE_PATH=/tmp # For files handler + +# ============================================================================== +# REDIS (Internal - Inside PHP Container) +# ============================================================================== +# Redis runs internally at 127.0.0.1:6379 inside PHP container +REDIS_HOST=127.0.0.1 +REDIS_PORT_INTERNAL=6379 # Internal Redis port (inside container) +REDIS_PASSWORD= # Empty for development +REDIS_DB=0 +REDIS_TIMEOUT=5 +REDIS_LOG_FILE=/var/log/redis.log + +# ============================================================================== +# NGINX (Internal - Inside PHP Container) +# ============================================================================== +NGINX_WORKER_PROCESSES=auto +NGINX_WORKER_CONNECTIONS=1024 +NGINX_CLIENT_MAX_BODY_SIZE=100M +NGINX_KEEPALIVE_TIMEOUT=65 + +# ============================================================================== +# COMPOSER +# ============================================================================== +COMPOSER_MEMORY_LIMIT=-1 # -1 for unlimited +COMPOSER_HOME=/root/.composer + +# ============================================================================== +# MEMCACHED (External Service) +# ============================================================================== +MEMCACHED_VERSION=1.6-alpine +MEMCACHED_MEMORY=256 # MB +MEMCACHED_MAX_CONNECTIONS=1024 +MEMCACHED_THREADS=4 +MEMCACHED_MAX_ITEM_SIZE=5m # 5 megabytes + +# Resource Limits +MEMCACHED_CPU_LIMIT=1.0 +MEMCACHED_CPU_RESERVATION=0.25 +MEMCACHED_MEMORY_TOTAL=512M +MEMCACHED_MEMORY_RESERVATION=256M + +# Health Check +MEMCACHED_HEALTHCHECK_INTERVAL=10s +MEMCACHED_HEALTHCHECK_TIMEOUT=5s +MEMCACHED_HEALTHCHECK_RETRIES=3 +MEMCACHED_HEALTHCHECK_START_PERIOD=10s + +# ============================================================================== +# FEATURES +# ============================================================================== +DEMO_MODE=false +HEALTH_CHECK_INSTALL=false + +# ============================================================================== +# NETWORK +# ============================================================================== +ENABLE_IPV6=false +BRIDGE_NAME=kariricode0 +NETWORK_MTU=1500 +NETWORK_SUBNET=172.20.0.0/16 +NETWORK_GATEWAY=172.20.0.1 + +# ============================================================================== +# LOGGING +# ============================================================================== +LOG_MAX_SIZE=10m # Maximum size before rotation +LOG_MAX_FILE=3 # Number of log files to keep +LOG_LEVEL=info # debug|info|notice|warning|error + +# ============================================================================== +# HEALTH CHECKS +# ============================================================================== +HEALTHCHECK_INTERVAL=30s +HEALTHCHECK_TIMEOUT=10s +HEALTHCHECK_RETRIES=3 +HEALTHCHECK_START_PERIOD=40s + +# ============================================================================== +# TEMPORARY FILESYSTEM +# ============================================================================== +TMPFS_SIZE=100M # Size of /tmp tmpfs + +# ============================================================================== +# TROUBLESHOOTING +# ============================================================================== +# Port conflicts? +# - Change APP_PORT, REDIS_PORT, or MEMCACHED_PORT +# - Run: make diagnose-ports +# - Run: make fix-ports +# +# Performance issues on macOS? +# - Try VOLUME_CONSISTENCY=delegated +# +# Permission errors? +# - Note: Container runs as root, but PHP-FPM/Nginx run as www-data +# - Check file ownership: ls -la +# +# Memory issues? +# - Adjust PHP_MEMORY_LIMIT and resource limits +# +# Session/Redis errors? +# - Use SESSION_SAVE_HANDLER=files for development +# - Use SESSION_SAVE_HANDLER=redis for distributed sessions +# +# PHP-FPM crashes? +# - Check logs: make logs SERVICE=php +# - Verify config: docker compose config +# +# Run diagnostics: +# - make env-check # Validate .env file +# - make diagnose-ports # Check port conflicts +# - make docker-info # Docker environment info +# - make status # Service status +# - make health # Health checks +# ============================================================================== + +# ============================================================================== +# PRODUCTION NOTES +# ============================================================================== +# For production deployment: +# 1. Change APP_ENV=production +# 2. Set APP_DEBUG=false +# 3. Generate strong APP_SECRET: openssl rand -hex 32 +# 4. Set XDEBUG_MODE=off +# 5. Set OPCACHE_VALIDATE_TIMESTAMPS=0 +# 6. Use SESSION_SAVE_HANDLER=redis with password +# 7. Set proper resource limits +# 8. Enable HTTPS/SSL termination +# 9. Use managed Redis/Memcached services +# 10. Implement proper backup strategy +# ============================================================================== \ No newline at end of file diff --git a/.make/core/Makefile.functions.mk b/.make/core/Makefile.functions.mk index 60cafed..81bb93c 100644 --- a/.make/core/Makefile.functions.mk +++ b/.make/core/Makefile.functions.mk @@ -7,28 +7,28 @@ # --- Validação de Parâmetros --- define validate_param @if [ -z "$($(1))" ]; then \ - echo "$(RED)✗ $(1) required. Usage: $(2)$(RESET)"; \ + echo -e "$(RED)✗ $(1) required. Usage: $(2)$(RESET)"; \ exit 1; \ fi endef # --- Execução Docker Genérica --- define docker_exec - @echo "$(BLUE)→ Running $(1) in Docker...$(RESET)" + @echo -e "$(BLUE)→ Running $(1) in Docker...$(RESET)" @$(DOCKER_RUN) $(DOCKER_IMAGE) $(2) - @echo "$(GREEN)✓ $(1) complete$(RESET)" + @echo -e "$(GREEN)✓ $(1) complete$(RESET)" endef define docker_exec_make - @echo "$(BLUE)→ Running make $(1) in Docker...$(RESET)" + @echo -e "$(BLUE)→ Running make $(1) in Docker...$(RESET)" @$(DOCKER_RUN) $(DOCKER_IMAGE) make $(1) - @echo "$(GREEN)✓ Docker make $(1) complete$(RESET)" + @echo -e "$(GREEN)✓ Docker make $(1) complete$(RESET)" endef # --- Verificação de Arquivo --- define check_file @test -f $(1) || (echo "$(RED)✗ $(2) not found$(RESET)" && exit 1) -endef +endef # --- Criação de Diretório --- define ensure_dir @@ -37,9 +37,9 @@ endef # --- Header de Pipeline --- define pipeline_header - @echo "$(BOLD)$(CYAN)╔════════════════════════════════════════════════════════╗$(RESET)" - @echo "$(BOLD)$(CYAN)║ $(1)$(RESET)" - @echo "$(BOLD)$(CYAN)╚════════════════════════════════════════════════════════╝$(RESET)" + @echo -e "$(BOLD)$(CYAN)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo -e "$(BOLD)$(CYAN)║ $(1)$(RESET)" + @echo -e "$(BOLD)$(CYAN)╚════════════════════════════════════════════════════════╝$(RESET)" @echo "" endef @@ -47,20 +47,20 @@ endef define check_git_branch @CURRENT_BRANCH=$$(git rev-parse --abbrev-ref HEAD); \ if [ "$$CURRENT_BRANCH" != "$(1)" ]; then \ - echo "$(RED)✗ This action requires branch '$(1)' (current: $$CURRENT_BRANCH)$(RESET)"; \ + echo -e "$(RED)✗ This action requires branch '$(1)' (current: $$CURRENT_BRANCH)$(RESET)"; \ exit 1; \ fi endef # --- Verificação de Versão PHP --- define check_php_version - @echo "$(BLUE)→ Checking PHP version...$(RESET)"; \ + @echo -e "$(BLUE)→ Checking PHP version...$(RESET)"; \ CURRENT_VERSION="$(PHP_VERSION)"; \ MIN_VERSION="$(PHP_MIN_VERSION)"; \ LOWEST=$$(printf '%s\n%s' "$$MIN_VERSION" "$$CURRENT_VERSION" | sort -V | head -n1); \ if [ "$$LOWEST" != "$$MIN_VERSION" ]; then \ - echo "$(RED)✗ PHP $$MIN_VERSION+ required, found $$CURRENT_VERSION$(RESET)"; \ + echo -e "$(RED)✗ PHP $$MIN_VERSION+ required, found $$CURRENT_VERSION$(RESET)"; \ exit 1; \ fi; \ - echo "$(GREEN)✓ PHP version $$CURRENT_VERSION OK (>= $$MIN_VERSION)$(RESET)" + echo -e "$(GREEN)✓ PHP version $$CURRENT_VERSION OK (>= $$MIN_VERSION)$(RESET)" endef diff --git a/.make/docker/Makefile.docker-compose.mk b/.make/docker/Makefile.docker-compose.mk index d5a8696..395da03 100644 --- a/.make/docker/Makefile.docker-compose.mk +++ b/.make/docker/Makefile.docker-compose.mk @@ -6,86 +6,315 @@ .PHONY: up down restart stop start status logs logs-follow ps \ exec-php exec-memcached health config validate-compose \ - up-build rebuild prune network-inspect + up-build rebuild prune network-inspect \ + check-ports diagnose-ports fix-ports kill-port port-scan up-safe \ + clean-docker check-docker-ports + +# Force bash for interactive commands +SHELL := /bin/bash + +# ============================================================================== +# DOCKER CLEANUP (executa antes das verificações de porta) +# ============================================================================== + +clean-docker: ## Clean orphaned Docker containers and networks + @echo -e "$(BLUE)→ Cleaning orphaned Docker resources...$(RESET)" + @echo -e "$(YELLOW) Removing stopped containers...$(RESET)" + @docker compose down 2>/dev/null || true + @docker container prune -f 2>/dev/null || true + @echo -e "$(YELLOW) Removing unused networks...$(RESET)" + @docker network prune -f 2>/dev/null || true + @echo -e "$(GREEN)✓ Docker cleanup complete$(RESET)" + +check-docker-ports: ## Check Docker containers using required ports + @echo -e "$(BLUE)→ Checking Docker containers for port conflicts...$(RESET)" + @bash -c ' \ + if [ -f .env ]; then \ + source .env 2>/dev/null || true; \ + fi; \ + APP_PORT=$${APP_PORT:-8089}; \ + REDIS_PORT=$${REDIS_PORT:-6379}; \ + MEMCACHED_PORT=$${MEMCACHED_PORT:-11211}; \ + CONFLICTS=0; \ + echo ""; \ + echo -e "$(CYAN)Checking all Docker containers (running and stopped):$(RESET)"; \ + for port in $$APP_PORT $$REDIS_PORT $$MEMCACHED_PORT; do \ + CONTAINERS=$$(docker ps -a --format "{{.Names}}\t{{.Ports}}" 2>/dev/null | grep ":$$port" || true); \ + if [ -n "$$CONTAINERS" ]; then \ + echo ""; \ + echo -e "$(RED)✗ Port $$port is bound to Docker containers:$(RESET)"; \ + echo "$$CONTAINERS" | while read line; do echo " $$line"; done; \ + CONFLICTS=$$((CONFLICTS + 1)); \ + else \ + echo -e "$(GREEN)✓ Port $$port not bound to Docker containers$(RESET)"; \ + fi; \ + done; \ + if [ $$CONFLICTS -gt 0 ]; then \ + echo ""; \ + echo -e "$(YELLOW)Run: make clean-docker$(RESET) to remove orphaned containers"; \ + exit 1; \ + fi \ + ' + +# ============================================================================== +# PORT CONFLICT DETECTION & RESOLUTION +# ============================================================================== + +check-ports: clean-docker check-docker-ports ## Check for port conflicts (includes Docker cleanup) + @echo -e "$(BLUE)→ Checking system ports for conflicts...$(RESET)" + @bash -c ' \ + if [ -f .env ]; then \ + source .env 2>/dev/null || true; \ + fi; \ + CONFLICTS=0; \ + PORTS="$${APP_PORT:-8089} $${REDIS_PORT:-6379} $${MEMCACHED_PORT:-11211}"; \ + for port in $$PORTS; do \ + if lsof -Pi :$$port -sTCP:LISTEN -t >/dev/null 2>&1; then \ + PROCESS=$$(lsof -Pi :$$port -sTCP:LISTEN -t | head -1); \ + CMD=$$(ps -p $$PROCESS -o comm= 2>/dev/null || echo "unknown"); \ + echo -e "$(RED)✗ Port $$port in use by system process PID $$PROCESS ($$CMD)$(RESET)"; \ + CONFLICTS=$$((CONFLICTS + 1)); \ + elif ss -ltn | grep -q ":$$port "; then \ + echo -e "$(RED)✗ Port $$port in use (detected by ss)$(RESET)"; \ + CONFLICTS=$$((CONFLICTS + 1)); \ + else \ + echo -e "$(GREEN)✓ Port $$port is available$(RESET)"; \ + fi; \ + done; \ + if [ $$CONFLICTS -gt 0 ]; then \ + echo ""; \ + echo -e "$(YELLOW)Resolution options:$(RESET)"; \ + echo -e " 1. Run: $(CYAN)make diagnose-ports$(RESET) for detailed info"; \ + echo -e " 2. Run: $(CYAN)make fix-ports$(RESET) to auto-resolve"; \ + echo -e " 3. Run: $(CYAN)make kill-port PORT=$(RESET) for specific port"; \ + exit 1; \ + fi; \ + echo -e "$(GREEN)✓ All ports available$(RESET)" \ + ' + +diagnose-ports: ## Detailed port conflict diagnosis + @echo -e "$(BOLD)$(CYAN)Port Conflict Diagnosis$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo "" + @bash -c ' \ + if [ -f .env ]; then \ + source .env 2>/dev/null || true; \ + fi; \ + APP_PORT=$${APP_PORT:-8089}; \ + REDIS_PORT=$${REDIS_PORT:-6379}; \ + MEMCACHED_PORT=$${MEMCACHED_PORT:-11211}; \ + echo -e "$(CYAN)Required Ports:$(RESET)"; \ + echo " APP_PORT: $$APP_PORT"; \ + echo " REDIS_PORT: $$REDIS_PORT"; \ + echo " MEMCACHED_PORT: $$MEMCACHED_PORT"; \ + echo ""; \ + echo -e "$(CYAN)System Port Status:$(RESET)"; \ + for port in $$APP_PORT $$REDIS_PORT $$MEMCACHED_PORT; do \ + echo ""; \ + echo -e "$(YELLOW)Port $$port:$(RESET)"; \ + if lsof -Pi :$$port -sTCP:LISTEN -t >/dev/null 2>&1; then \ + lsof -Pi :$$port -sTCP:LISTEN | awk "NR>1 {printf \" PID: %s | Command: %s | User: %s\n\", \$$2, \$$1, \$$3}"; \ + echo -e " $(RED)Status: IN USE (lsof)$(RESET)"; \ + elif ss -ltn | grep -q ":$$port "; then \ + echo -e " $(RED)Status: IN USE (ss)$(RESET)"; \ + ss -ltnp | grep ":$$port " | awk "{print \" \" \$$0}"; \ + else \ + echo -e " $(GREEN)Status: AVAILABLE$(RESET)"; \ + fi; \ + done; \ + echo ""; \ + echo -e "$(CYAN)Docker Containers (All):$(RESET)"; \ + docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null | grep -E "$$APP_PORT|$$REDIS_PORT|$$MEMCACHED_PORT" || echo " None found"; \ + echo ""; \ + echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)"; \ + echo ""; \ + echo -e "$(CYAN)Suggested Actions:$(RESET)"; \ + echo -e " 1. $(YELLOW)make clean-docker$(RESET) - Remove orphaned Docker containers"; \ + echo -e " 2. $(YELLOW)make fix-ports$(RESET) - Auto-fix all conflicts"; \ + echo -e " 3. $(YELLOW)make kill-port PORT=$(RESET) - Kill specific process"; \ + echo " 4. Edit .env to change ports"; \ + ' + +fix-ports: ## Automatically fix port conflicts (interactive) + @echo -e "$(YELLOW)⚠ This will attempt to free up conflicting ports$(RESET)" + @echo -e "$(YELLOW) Docker containers and processes will be terminated$(RESET)" + @echo "" + @bash -c ' \ + read -p "Continue? [y/N] " -n 1 -r; \ + echo; \ + if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ + $(MAKE) --no-print-directory clean-docker; \ + $(MAKE) --no-print-directory _do_fix_ports; \ + else \ + echo -e "$(YELLOW)Cancelled$(RESET)"; \ + fi \ + ' + +_do_fix_ports: + @echo -e "$(BLUE)→ Scanning and fixing system port conflicts...$(RESET)" + @bash -c ' \ + if [ -f .env ]; then \ + source .env 2>/dev/null || true; \ + fi; \ + PORTS="$${APP_PORT:-8089} $${REDIS_PORT:-6379} $${MEMCACHED_PORT:-11211}"; \ + for port in $$PORTS; do \ + if lsof -Pi :$$port -sTCP:LISTEN -t >/dev/null 2>&1; then \ + PROCESS=$$(lsof -Pi :$$port -sTCP:LISTEN -t | head -1); \ + CMD=$$(ps -p $$PROCESS -o comm= 2>/dev/null || echo "unknown"); \ + echo -e "$(YELLOW)→ Terminating process $$PROCESS ($$CMD) on port $$port...$(RESET)"; \ + kill -15 $$PROCESS 2>/dev/null && sleep 1; \ + if lsof -Pi :$$port -sTCP:LISTEN -t >/dev/null 2>&1; then \ + echo -e "$(RED) Process didnt stop gracefully, forcing...$(RESET)"; \ + kill -9 $$PROCESS 2>/dev/null || sudo kill -9 $$PROCESS 2>/dev/null || true; \ + fi; \ + if ! lsof -Pi :$$port -sTCP:LISTEN -t >/dev/null 2>&1; then \ + echo -e "$(GREEN)✓ Port $$port freed$(RESET)"; \ + else \ + echo -e "$(RED)✗ Failed to free port $$port$(RESET)"; \ + fi; \ + fi; \ + done \ + ' + @echo "" + @$(MAKE) --no-print-directory check-ports + +kill-port: ## Kill process using specific port (usage: make kill-port PORT=11211) + @if [ -z "$(PORT)" ]; then \ + echo -e "$(RED)✗ PORT parameter required$(RESET)"; \ + echo -e "$(YELLOW)Usage: make kill-port PORT=11211$(RESET)"; \ + exit 1; \ + fi + @echo -e "$(BLUE)→ Checking port $(PORT)...$(RESET)" + @bash -c ' \ + if lsof -Pi :$(PORT) -sTCP:LISTEN -t >/dev/null 2>&1; then \ + PROCESS=$$(lsof -Pi :$(PORT) -sTCP:LISTEN -t | head -1); \ + CMD=$$(ps -p $$PROCESS -o comm= 2>/dev/null || echo "unknown"); \ + echo -e "$(YELLOW)Found process: PID $$PROCESS ($$CMD)$(RESET)"; \ + echo -e "$(YELLOW)Attempting graceful shutdown...$(RESET)"; \ + kill -15 $$PROCESS 2>/dev/null && sleep 2; \ + if lsof -Pi :$(PORT) -sTCP:LISTEN -t >/dev/null 2>&1; then \ + echo -e "$(RED)Process didnt stop, forcing shutdown...$(RESET)"; \ + kill -9 $$PROCESS 2>/dev/null || sudo kill -9 $$PROCESS 2>/dev/null || true; \ + fi; \ + if ! lsof -Pi :$(PORT) -sTCP:LISTEN -t >/dev/null 2>&1; then \ + echo -e "$(GREEN)✓ Port $(PORT) is now free$(RESET)"; \ + else \ + echo -e "$(RED)✗ Failed to free port $(PORT)$(RESET)"; \ + fi; \ + else \ + echo -e "$(GREEN)✓ Port $(PORT) is already free$(RESET)"; \ + fi \ + ' + +port-scan: ## Scan common ports for conflicts + @echo -e "$(BOLD)$(CYAN)Port Scanner$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo "" + @for port in 80 443 3000 3306 5432 6379 8000 8080 8089 9000 11211 27017; do \ + if lsof -Pi :$$port -sTCP:LISTEN -t >/dev/null 2>&1; then \ + PROCESS=$$(lsof -Pi :$$port -sTCP:LISTEN -t | head -1); \ + CMD=$$(ps -p $$PROCESS -o comm= 2>/dev/null || echo "unknown"); \ + echo -e "$(RED)✗ Port $$port: IN USE$(RESET) (PID $$PROCESS - $$CMD)"; \ + else \ + echo -e "$(GREEN)✓ Port $$port: Available$(RESET)"; \ + fi; \ + done + @echo "" + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" # ============================================================================== # LIFECYCLE MANAGEMENT # ============================================================================== +up-safe: check-ports up ## Start services after checking ports (recommended) + up: ## Start Docker Compose services - @echo "$(BLUE)→ Starting Docker Compose services...$(RESET)" + @echo -e "$(BLUE)→ Starting Docker Compose services...$(RESET)" @if [ ! -f .env ]; then \ - echo "$(YELLOW)⚠ .env not found, copying from .env.example...$(RESET)"; \ - cp .env.example .env 2>/dev/null || echo "$(RED)✗ .env.example not found$(RESET)"; \ + echo -e "$(YELLOW)⚠ .env not found, copying from .env.example...$(RESET)"; \ + cp .env.example .env 2>/dev/null || echo -e "$(RED)✗ .env.example not found$(RESET)"; \ fi - @docker compose up -d - @echo "$(GREEN)✓ Services started$(RESET)" + @docker compose --profile development up -d || { \ + echo ""; \ + echo -e "$(RED)✗ Failed to start services$(RESET)"; \ + echo -e "$(YELLOW)Possible port conflict with orphaned containers$(RESET)"; \ + echo -e "$(YELLOW)Run 'make diagnose-ports' for analysis$(RESET)"; \ + echo -e "$(YELLOW)Run 'make clean-docker' to remove orphaned containers$(RESET)"; \ + exit 1; \ + } + @echo -e "$(GREEN)✓ Services started$(RESET)" + @sleep 2 @$(MAKE) --no-print-directory status up-build: ## Start services with build - @echo "$(BLUE)→ Building and starting Docker Compose services...$(RESET)" - @docker compose up -d --build - @echo "$(GREEN)✓ Services built and started$(RESET)" + @echo -e "$(BLUE)→ Building and starting Docker Compose services...$(RESET)" + @docker compose --profile development up -d --build || { \ + echo ""; \ + echo -e "$(RED)✗ Failed to build/start services$(RESET)"; \ + echo -e "$(YELLOW)Run 'make clean-docker' to clean orphaned containers$(RESET)"; \ + exit 1; \ + } + @echo -e "$(GREEN)✓ Services built and started$(RESET)" @$(MAKE) --no-print-directory status down: ## Stop and remove Docker Compose services - @echo "$(BLUE)→ Stopping Docker Compose services...$(RESET)" - @docker compose down - @echo "$(GREEN)✓ Services stopped and removed$(RESET)" + @echo -e "$(BLUE)→ Stopping Docker Compose services...$(RESET)" + @docker compose down -v --remove-orphans + @echo -e "$(GREEN)✓ Services stopped and removed$(RESET)" stop: ## Stop Docker Compose services (without removing) - @echo "$(BLUE)→ Stopping Docker Compose services...$(RESET)" + @echo -e "$(BLUE)→ Stopping Docker Compose services...$(RESET)" @docker compose stop - @echo "$(GREEN)✓ Services stopped$(RESET)" + @echo -e "$(GREEN)✓ Services stopped$(RESET)" start: ## Start existing Docker Compose services - @echo "$(BLUE)→ Starting existing Docker Compose services...$(RESET)" + @echo -e "$(BLUE)→ Starting existing Docker Compose services...$(RESET)" @docker compose start - @echo "$(GREEN)✓ Services started$(RESET)" + @echo -e "$(GREEN)✓ Services started$(RESET)" restart: ## Restart Docker Compose services - @echo "$(BLUE)→ Restarting Docker Compose services...$(RESET)" + @echo -e "$(BLUE)→ Restarting Docker Compose services...$(RESET)" @docker compose restart - @echo "$(GREEN)✓ Services restarted$(RESET)" + @echo -e "$(GREEN)✓ Services restarted$(RESET)" rebuild: down clean-volumes up-build ## Rebuild environment from scratch - @echo "$(GREEN)✓ Environment rebuilt$(RESET)" + @echo -e "$(GREEN)✓ Environment rebuilt$(RESET)" # ============================================================================== # MONITORING & INSPECTION # ============================================================================== status: ## Show services status - @echo "$(BOLD)$(CYAN)Docker Compose Services Status$(RESET)" - @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo -e "$(BOLD)$(CYAN)Docker Compose Services Status$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" @docker compose ps - @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" ps: status ## Alias for status logs: ## Show logs from all services (usage: make logs SERVICE=php) @if [ -n "$(SERVICE)" ]; then \ - echo "$(BLUE)→ Showing logs for service: $(SERVICE)$(RESET)"; \ + echo -e "$(BLUE)→ Showing logs for service: $(SERVICE)$(RESET)"; \ docker compose logs $(SERVICE); \ else \ - echo "$(BLUE)→ Showing logs for all services$(RESET)"; \ + echo -e "$(BLUE)→ Showing logs for all services$(RESET)"; \ docker compose logs; \ fi logs-follow: ## Follow logs from services (usage: make logs-follow SERVICE=php) @if [ -n "$(SERVICE)" ]; then \ - echo "$(BLUE)→ Following logs for service: $(SERVICE)$(RESET)"; \ + echo -e "$(BLUE)→ Following logs for service: $(SERVICE)$(RESET)"; \ docker compose logs -f $(SERVICE); \ else \ - echo "$(BLUE)→ Following logs for all services$(RESET)"; \ + echo -e "$(BLUE)→ Following logs for all services$(RESET)"; \ docker compose logs -f; \ fi health: ## Check services health status - @echo "$(BOLD)$(CYAN)Services Health Check$(RESET)" - @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" - @docker compose ps --format json | jq -r '.[] | "\(.Name): \(.Health // "N/A") - \(.State)"' - @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + @echo -e "$(BOLD)$(CYAN)Services Health Check$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker compose ps --format json 2>/dev/null | jq -r '.[] | "\(.Name): \(.Health // "N/A") - \(.State)"' 2>/dev/null || docker compose ps + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" # ============================================================================== # CONTAINER INTERACTION @@ -93,15 +322,15 @@ health: ## Check services health status exec-php: ## Execute command in PHP container (usage: make exec-php CMD="php -v") @if [ -z "$(CMD)" ]; then \ - echo "$(BLUE)→ Opening interactive shell in PHP container...$(RESET)"; \ + echo -e "$(BLUE)→ Opening interactive shell in PHP container...$(RESET)"; \ docker compose exec php /bin/bash; \ else \ - echo "$(BLUE)→ Executing: $(CMD)$(RESET)"; \ + echo -e "$(BLUE)→ Executing: $(CMD)$(RESET)"; \ docker compose exec php $(CMD); \ fi exec-memcached: ## Execute command in Memcached container - @echo "$(BLUE)→ Connecting to Memcached container...$(RESET)" + @echo -e "$(BLUE)→ Connecting to Memcached container...$(RESET)" @docker compose exec memcached sh # ============================================================================== @@ -109,44 +338,46 @@ exec-memcached: ## Execute command in Memcached container # ============================================================================== config: ## Validate and view Docker Compose configuration - @echo "$(BLUE)→ Validating Docker Compose configuration...$(RESET)" + @echo -e "$(BLUE)→ Validating Docker Compose configuration...$(RESET)" @docker compose config - @echo "$(GREEN)✓ Configuration is valid$(RESET)" + @echo -e "$(GREEN)✓ Configuration is valid$(RESET)" validate-compose: ## Validate docker-compose.yml syntax - @echo "$(BLUE)→ Validating docker-compose.yml...$(RESET)" + @echo -e "$(BLUE)→ Validating docker-compose.yml...$(RESET)" @docker compose config --quiet && \ - echo "$(GREEN)✓ docker-compose.yml is valid$(RESET)" || \ - (echo "$(RED)✗ docker-compose.yml has errors$(RESET)" && exit 1) + echo -e "$(GREEN)✓ docker-compose.yml is valid$(RESET)" || \ + (echo -e "$(RED)✗ docker-compose.yml has errors$(RESET)" && exit 1) # ============================================================================== # NETWORK & RESOURCES # ============================================================================== network-inspect: ## Inspect Docker network - @echo "$(BOLD)$(CYAN)Docker Network Information$(RESET)" - @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo -e "$(BOLD)$(CYAN)Docker Network Information$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" @docker network inspect $$(docker compose config --format json | jq -r '.networks | keys[0]') 2>/dev/null || \ - echo "$(YELLOW)⚠ Network not found or not created yet$(RESET)" - @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + echo -e "$(YELLOW)⚠ Network not found or not created yet$(RESET)" + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" prune: ## Remove unused Docker resources - @echo "$(YELLOW)⚠ This will remove all unused containers, networks, and volumes$(RESET)" - @echo "$(BLUE)→ Pruning Docker resources...$(RESET)" + @echo -e "$(YELLOW)⚠ This will remove all unused containers, networks, and volumes$(RESET)" + @echo -e "$(BLUE)→ Pruning Docker resources...$(RESET)" @docker system prune -f --volumes - @echo "$(GREEN)✓ Docker resources pruned$(RESET)" + @echo -e "$(GREEN)✓ Docker resources pruned$(RESET)" clean-volumes: ## Remove all volumes (WARNING: data loss) - @echo "$(RED)⚠ WARNING: This will delete ALL volume data!$(RESET)" - @read -p "Are you sure? [y/N] " -n 1 -r; \ + @echo -e "$(RED)⚠ WARNING: This will delete ALL volume data!$(RESET)" + @bash -c ' \ + read -p "Are you sure? [y/N] " -n 1 -r; \ echo; \ if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ - echo "$(BLUE)→ Removing all volumes...$(RESET)"; \ + echo -e "$(BLUE)→ Removing all volumes...$(RESET)"; \ docker compose down -v; \ - echo "$(GREEN)✓ Volumes removed$(RESET)"; \ + echo -e "$(GREEN)✓ Volumes removed$(RESET)"; \ else \ - echo "$(YELLOW)Cancelled$(RESET)"; \ - fi + echo -e "$(YELLOW)Cancelled$(RESET)"; \ + fi \ + ' # ============================================================================== # QUICK ACTIONS @@ -155,17 +386,17 @@ clean-volumes: ## Remove all volumes (WARNING: data loss) shell: exec-php ## Alias for exec-php (open shell) composer-install: ## Run composer install in PHP container - @echo "$(BLUE)→ Running composer install...$(RESET)" + @echo -e "$(BLUE)→ Running composer install...$(RESET)" @docker compose exec php composer install --no-interaction --prefer-dist --optimize-autoloader - @echo "$(GREEN)✓ Composer dependencies installed$(RESET)" + @echo -e "$(GREEN)✓ Composer dependencies installed$(RESET)" composer-update: ## Run composer update in PHP container - @echo "$(BLUE)→ Running composer update...$(RESET)" + @echo -e "$(BLUE)→ Running composer update...$(RESET)" @docker compose exec php composer update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader - @echo "$(GREEN)✓ Composer dependencies updated$(RESET)" + @echo -e "$(GREEN)✓ Composer dependencies updated$(RESET)" test-compose: up ## Start services and run tests - @echo "$(BLUE)→ Waiting for services to be ready...$(RESET)" + @echo -e "$(BLUE)→ Waiting for services to be ready...$(RESET)" @sleep 3 @$(MAKE) --no-print-directory exec-php CMD="make test" @@ -174,27 +405,27 @@ test-compose: up ## Start services and run tests # ============================================================================== ports: ## Show exposed ports - @echo "$(BOLD)$(CYAN)Exposed Ports$(RESET)" - @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" - @docker compose ps --format "table {{.Name}}\t{{.Ports}}" - @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + @echo -e "$(BOLD)$(CYAN)Exposed Ports$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @docker compose ps --format "table {{.Names}}\t{{.Ports}}" + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" inspect-php: ## Inspect PHP container - @echo "$(BOLD)$(CYAN)PHP Container Inspection$(RESET)" - @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo -e "$(BOLD)$(CYAN)PHP Container Inspection$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" @docker compose exec php php -v @echo "" @docker compose exec php php -m - @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" env-check: ## Verify environment variables - @echo "$(BOLD)$(CYAN)Environment Configuration$(RESET)" - @echo "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" + @echo -e "$(BOLD)$(CYAN)Environment Configuration$(RESET)" + @echo -e "$(BLUE)╔════════════════════════════════════════════════════════╗$(RESET)" @if [ -f .env ]; then \ - echo "$(GREEN)✓ .env file exists$(RESET)"; \ + echo -e "$(GREEN)✓ .env file exists$(RESET)"; \ grep -E '^[A-Z_]+=' .env | head -20; \ else \ - echo "$(RED)✗ .env file not found$(RESET)"; \ - echo "$(YELLOW) Run: cp .env.example .env$(RESET)"; \ + echo -e "$(RED)✗ .env file not found$(RESET)"; \ + echo -e "$(YELLOW) Run: cp .env.example .env$(RESET)"; \ fi - @echo "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" \ No newline at end of file + @echo -e "$(BLUE)╚════════════════════════════════════════════════════════╝$(RESET)" \ No newline at end of file diff --git a/.make/local/Makefile.helpers.mk b/.make/local/Makefile.helpers.mk index cafd966..43552e1 100644 --- a/.make/local/Makefile.helpers.mk +++ b/.make/local/Makefile.helpers.mk @@ -84,93 +84,93 @@ bench: ## Run benchmarks (unified target). See 'make bench-help'. @$(BENCH_SCRIPT) bench-help: ## Show usage help for the unified bench command - @echo "$(BOLD)$(CYAN)Benchmark Command Help$(RESET)" - @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" - @echo "Single entrypoint: $(BOLD)make bench$(RESET)" - @echo "" - @echo "$(BOLD)Parameters:$(RESET)" - @echo " $(CYAN)REF=auto|main|$(RESET) Compare against a stored tag" - @echo " auto → try 'main' if available (default)" - @echo " main → compare against main reference" - @echo " → compare against any other tag" - @echo "" - @echo " $(CYAN)STORE=1 TAG=$(RESET) Store benchmarks with a given tag" - @echo " ex.: STORE=1 TAG=feature-x" - @echo "" - @echo " $(CYAN)ENFORCE_MAIN=1$(RESET) When TAG=main, ensures you are on branch 'main'" - @echo "" - @echo " $(CYAN)REPORT=1$(RESET) Save text output to $(BENCH_REPORT_DIR)/last.txt" - @echo "" - @echo "$(BOLD)Examples:$(RESET)" - @echo " make bench # Run normal benchmarks" - @echo " make bench REF=main # Compare against 'main'" - @echo " make bench REF=my-tag # Compare against custom tag" - @echo " make bench STORE=1 TAG=feat # Store results under tag 'feat'" - @echo " make bench STORE=1 TAG=main ENFORCE_MAIN=1 # Store results as 'main'" - @echo " make bench REF=main REPORT=1 # Compare and save output to last.txt" - @echo "" - @echo "$(GREEN)✓ Tip: Run 'make bench-help' anytime to see this guide$(RESET)" + @echo -e "$(BOLD)$(CYAN)Benchmark Command Help$(RESET)" + @echo -e "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo -e "Single entrypoint: $(BOLD)make bench$(RESET)" + @echo -e "" + @echo -e "$(BOLD)Parameters:$(RESET)" + @echo -e " $(CYAN)REF=auto|main|$(RESET) Compare against a stored tag" + @echo -e " auto → try 'main' if available (default)" + @echo -e " main → compare against main reference" + @echo -e " → compare against any other tag" + @echo -e "" + @echo -e " $(CYAN)STORE=1 TAG=$(RESET) Store benchmarks with a given tag" + @echo -e " ex.: STORE=1 TAG=feature-x" + @echo -e "" + @echo -e " $(CYAN)ENFORCE_MAIN=1$(RESET) When TAG=main, ensures you are on branch 'main'" + @echo -e "" + @echo -e " $(CYAN)REPORT=1$(RESET) Save text output to $(BENCH_REPORT_DIR)/last.txt" + @echo -e "" + @echo -e "$(BOLD)Examples:$(RESET)" + @echo -e " make bench # Run normal benchmarks" + @echo -e " make bench REF=main # Compare against 'main'" + @echo -e " make bench REF=my-tag # Compare against custom tag" + @echo -e " make bench STORE=1 TAG=feat # Store results under tag 'feat'" + @echo -e " make bench STORE=1 TAG=main ENFORCE_MAIN=1 # Store results as 'main'" + @echo -e " make bench REF=main REPORT=1 # Compare and save output to last.txt" + @echo -e "" + @echo -e "$(GREEN)✓ Tip: Run 'make bench-help' anytime to see this guide$(RESET)" # ============================================================================== # DEVELOPMENT HELPERS # ============================================================================== git-hooks-setup: ## Setup git hooks for development workflow - @echo "$(BLUE)→ Setting up git hooks...$(RESET)" + @echo -e "$(BLUE)→ Setting up git hooks...$(RESET)" @mkdir -p .git/hooks @if [ -f .git/hooks/pre-commit ] && [ ! -f .git/hooks/pre-commit.bak ]; then \ - echo "$(YELLOW)⚠ Existing pre-commit hook found. Backing up...$(RESET)"; \ + echo -e "$(YELLOW)⚠ Existing pre-commit hook found. Backing up...$(RESET)"; \ mv .git/hooks/pre-commit .git/hooks/pre-commit.bak; \ fi @echo '#!/bin/sh' > .git/hooks/pre-commit @echo 'set -e' >> .git/hooks/pre-commit @echo 'make pre-commit' >> .git/hooks/pre-commit @chmod +x .git/hooks/pre-commit - @echo "$(GREEN)✓ Git hooks set up$(RESET)" + @echo -e "$(GREEN)✓ Git hooks set up$(RESET)" git-hooks-remove: ## Remove git hooks and restore backups if any - @echo "$(BLUE)→ Cleaning up git hooks...$(RESET)" + @echo -e "$(BLUE)→ Cleaning up git hooks...$(RESET)" @if [ -f .git/hooks/pre-commit.bak ]; then \ - echo "$(YELLOW)↩ Restoring backup pre-commit hook...$(RESET)"; \ + echo -e "$(YELLOW)↩ Restoring backup pre-commit hook...$(RESET)"; \ mv .git/hooks/pre-commit.bak .git/hooks/pre-commit; \ elif [ -f .git/hooks/pre-commit ]; then \ - echo "$(RED)✗ Removing generated pre-commit hook...$(RESET)"; \ + echo -e "$(RED)✗ Removing generated pre-commit hook...$(RESET)"; \ rm .git/hooks/pre-commit; \ else \ - echo "$(YELLOW)⚠ No pre-commit hook found$(RESET)"; \ + echo -e "$(YELLOW)⚠ No pre-commit hook found$(RESET)"; \ fi - @echo "$(GREEN)✓ Git hooks cleaned$(RESET)" + @echo -e "$(GREEN)✓ Git hooks cleaned$(RESET)" git-hooks-check: ## Check if git hooks are installed correctly - @echo "$(BLUE)→ Verifying git hooks...$(RESET)" + @echo -e "$(BLUE)→ Verifying git hooks...$(RESET)" @if [ -f .git/hooks/pre-commit ]; then \ if grep -q "make pre-commit" .git/hooks/pre-commit; then \ - echo "$(GREEN)✓ pre-commit hook is installed correctly$(RESET)"; \ + echo -e "$(GREEN)✓ pre-commit hook is installed correctly$(RESET)"; \ else \ - echo "$(RED)✗ pre-commit hook exists but was not installed by this Makefile$(RESET)"; \ + echo -e "$(RED)✗ pre-commit hook exists but was not installed by this Makefile$(RESET)"; \ fi \ else \ - echo "$(RED)✗ pre-commit hook not found$(RESET)"; \ + echo -e "$(RED)✗ pre-commit hook not found$(RESET)"; \ fi info: ## Show PHP and project information - @echo "$(BOLD)$(CYAN)Project Information$(RESET)" - @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" - @echo "PHP Version: $(PHP_VERSION)" - @echo "PHP Binary: $(PHP)" - @echo "Composer: $(COMPOSER)" - @echo "Project Directory: $(shell pwd)" - @echo "Source Directory: $(SRC_DIR)" - @echo "Test Directory: $(TEST_DIR)" - @echo "" - @echo "$(BOLD)$(CYAN)Installed Tools$(RESET)" - @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" - @test -f $(PHPUNIT) && echo "PHPUnit: ✓" || echo "PHPUnit: ✗" - @test -f $(PHPSTAN) && echo "PHPStan: ✓" || echo "PHPStan: ✗" - @test -f $(PSALM) && echo "Psalm: ✓" || echo "Psalm: ✗" - @test -f $(PHPCS) && echo "PHPCS: ✓" || echo "PHPCS: ✗" - @test -f $(PHP_CS_FIXER) && echo "PHP-CS-Fixer: ✓" || echo "PHP-CS-Fixer: ✗" - @test -f $(INFECTION) && echo "Infection: ✓" || echo "Infection: ✗" - @test -f $(PHPBENCH) && echo "PHPBench: ✓" || echo "PHPBench: ✗" + @echo -e "$(BOLD)$(CYAN)Project Information$(RESET)" + @echo -e "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo -e "PHP Version: $(PHP_VERSION)" + @echo -e "PHP Binary: $(PHP)" + @echo -e "Composer: $(COMPOSER)" + @echo -e "Project Directory: $(shell pwd)" + @echo -e "Source Directory: $(SRC_DIR)" + @echo -e "Test Directory: $(TEST_DIR)" + @echo -e "" + @echo -e "$(BOLD)$(CYAN)Installed Tools$(RESET)" + @echo -e "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @test -f $(PHPUNIT) && echo -e "PHPUnit: ✓" || echo -e "PHPUnit: ✗" + @test -f $(PHPSTAN) && echo -e "PHPStan: ✓" || echo -e "PHPStan: ✗" + @test -f $(PSALM) && echo -e "Psalm: ✓" || echo -e "Psalm: ✗" + @test -f $(PHPCS) && echo -e "PHPCS: ✓" || echo -e "PHPCS: ✗" + @test -f $(PHP_CS_FIXER) && echo -e "PHP-CS-Fixer: ✓" || echo -e "PHP-CS-Fixer: ✗" + @test -f $(INFECTION) && echo -e "Infection: ✓" || echo -e "Infection: ✗" + @test -f $(PHPBENCH) && echo -e "PHPBench: ✓" || echo -e "PHPBench: ✗" # ============================================================================== # RELEASE MANAGEMENT @@ -178,44 +178,44 @@ info: ## Show PHP and project information tag: ## Create a new git tag (usage: make tag VERSION=1.0.0) @if [ -z "$(VERSION)" ]; then \ - echo "$(RED)✗ VERSION is required. Usage: make tag VERSION=1.0.0$(RESET)"; \ + echo -e "$(RED)✗ VERSION is required. Usage: make tag VERSION=1.0.0$(RESET)"; \ exit 1; \ fi - @echo "$(BLUE)→ Creating tag v$(VERSION)...$(RESET)" + @echo -e "$(BLUE)→ Creating tag v$(VERSION)...$(RESET)" @git tag -a "v$(VERSION)" -m "Release v$(VERSION)" @git push origin "v$(VERSION)" - @echo "$(GREEN)✓ Tag v$(VERSION) created and pushed$(RESET)" + @echo -e "$(GREEN)✓ Tag v$(VERSION) created and pushed$(RESET)" release: cd ## Prepare release (run full CD pipeline) - @echo "$(BOLD)$(GREEN)✓ Release preparation complete$(RESET)" + @echo -e "$(BOLD)$(GREEN)✓ Release preparation complete$(RESET)" @echo "" - @echo "$(CYAN)Next steps:$(RESET)" - @echo " 1. Update CHANGELOG.md" - @echo " 2. Update version in composer.json" - @echo " 3. Commit changes" - @echo " 4. Run: make tag VERSION=X.Y.Z" - @echo " 5. Push to GitHub" - @echo " 6. Create GitHub release" + @echo -e "$(CYAN)Next steps:$(RESET)" + @echo -e " 1. Update CHANGELOG.md" + @echo -e " 2. Update version in composer.json" + @echo -e " 3. Commit changes" + @echo -e " 4. Run: make tag VERSION=X.Y.Z" + @echo -e " 5. Push to GitHub" + @echo -e " 6. Create GitHub release" # ============================================================================== # STATS & METRICS # ============================================================================== stats: ## Show project statistics - @echo "$(BOLD)$(CYAN)Project Statistics$(RESET)" - @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" - @echo "Total PHP files: $$(find $(SRC_DIR) -name '*.php' | wc -l)" - @echo "Total test files: $$(find $(TEST_DIR) -name '*.php' | wc -l)" - @echo "Lines of code: $$(find $(SRC_DIR) -name '*.php' -exec cat {} \; | wc -l)" - @echo "Lines of tests: $$(find $(TEST_DIR) -name '*.php' -exec cat {} \; | wc -l)" - @echo "" - @echo "$(BOLD)$(CYAN)Directory Sizes$(RESET)" - @echo "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo -e "$(BOLD)$(CYAN)Project Statistics$(RESET)" + @echo -e "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" + @echo -e "Total PHP files: $$(find $(SRC_DIR) -name '*.php' | wc -l)" + @echo -e "Total test files: $$(find $(TEST_DIR) -name '*.php' | wc -l)" + @echo -e "Lines of code: $$(find $(SRC_DIR) -name '*.php' -exec cat {} \; | wc -l)" + @echo -e "Lines of tests: $$(find $(TEST_DIR) -name '*.php' -exec cat {} \; | wc -l)" + @echo -e "" + @echo -e "$(BOLD)$(CYAN)Directory Sizes$(RESET)" + @echo -e "$(BLUE)═══════════════════════════════════════════════════════════$(RESET)" @du -sh $(SRC_DIR) 2>/dev/null || true @du -sh $(TEST_DIR) 2>/dev/null || true @du -sh vendor 2>/dev/null || true loc: ## Count lines of code - @echo "$(BLUE)→ Counting lines of code...$(RESET)" + @echo -e "$(BLUE)→ Counting lines of code...$(RESET)" @find $(SRC_DIR) -name '*.php' -exec wc -l {} \; | awk '{sum += $$1} END {print "Source: " sum " lines"}' @find $(TEST_DIR) -name '*.php' -exec wc -l {} \; | awk '{sum += $$1} END {print "Tests: " sum " lines"}' diff --git a/.make/local/Makefile.setup.mk b/.make/local/Makefile.setup.mk index c759e71..8441953 100644 --- a/.make/local/Makefile.setup.mk +++ b/.make/local/Makefile.setup.mk @@ -13,48 +13,48 @@ check-php: ## Check PHP version requirement $(call check_php_version) install: check-php ## Install dependencies - @echo "$(BLUE)→ Installing Composer dependencies...$(RESET)" + @echo -e "$(BLUE)→ Installing Composer dependencies...$(RESET)" @if ! $(COMPOSER) validate --no-check-publish 2>/dev/null; then \ - echo "$(YELLOW)⚠ composer.lock outdated, updating dependencies...$(RESET)"; \ + echo -e "$(YELLOW)⚠ composer.lock outdated, updating dependencies...$(RESET)"; \ $(COMPOSER) update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader; \ else \ $(COMPOSER) install --no-interaction --prefer-dist --optimize-autoloader; \ fi @$(MAKE) verify-install - @echo "$(GREEN)✓ Installation complete$(RESET)" + @echo -e "$(GREEN)✓ Installation complete$(RESET)" install-dev: check-php ## Install dependencies with dev tools - @echo "$(BLUE)→ Installing Composer dependencies (dev mode)...$(RESET)" + @echo -e "$(BLUE)→ Installing Composer dependencies (dev mode)...$(RESET)" @$(COMPOSER) install --no-interaction --prefer-dist @$(MAKE) verify-install - @echo "$(GREEN)✓ Development installation complete$(RESET)" + @echo -e "$(GREEN)✓ Development installation complete$(RESET)" fresh-install: check-php ## Fresh install (removes lock file) - @echo "$(BLUE)→ Removing composer.lock...$(RESET)" + @echo -e "$(BLUE)→ Removing composer.lock...$(RESET)" @rm -f composer.lock - @echo "$(BLUE)→ Installing fresh dependencies...$(RESET)" + @echo -e "$(BLUE)→ Installing fresh dependencies...$(RESET)" @$(COMPOSER) install --no-interaction --prefer-dist --optimize-autoloader @$(MAKE) verify-install - @echo "$(GREEN)✓ Fresh installation complete$(RESET)" + @echo -e "$(GREEN)✓ Fresh installation complete$(RESET)" update: ## Update dependencies - @echo "$(BLUE)→ Updating Composer dependencies...$(RESET)" + @echo -e "$(BLUE)→ Updating Composer dependencies...$(RESET)" @$(COMPOSER) update --with-all-dependencies --no-interaction --prefer-dist --optimize-autoloader - @echo "$(GREEN)✓ Dependencies updated$(RESET)" + @echo -e "$(GREEN)✓ Dependencies updated$(RESET)" verify-install: ## Verify installation - @echo "$(BLUE)→ Verifying installation...$(RESET)" + @echo -e "$(BLUE)→ Verifying installation...$(RESET)" $(call check_file,vendor/autoload.php,Autoloader) $(call check_file,$(PHPUNIT),PHPUnit) $(call check_file,$(PHPSTAN),PHPStan) - @echo "$(GREEN)✓ Installation verified$(RESET)" + @echo -e "$(GREEN)✓ Installation verified$(RESET)" # ============================================================================== # CLEANUP # ============================================================================== clean: ## Clean build artifacts and caches - @echo "$(BLUE)→ Cleaning build artifacts...$(RESET)" + @echo -e "$(BLUE)→ Cleaning build artifacts...$(RESET)" @rm -rf $(BUILD_DIR) @rm -rf $(COVERAGE_DIR) @rm -rf $(REPORTS_DIR) @@ -63,47 +63,47 @@ clean: ## Clean build artifacts and caches @rm -rf .phpunit.result.cache @rm -rf .php-cs-fixer.cache @rm -f infection.log infection.html - @echo "$(GREEN)✓ Clean complete$(RESET)" + @echo -e "$(GREEN)✓ Clean complete$(RESET)" clean-all: clean ## Clean everything including vendor - @echo "$(BLUE)→ Removing vendor directory...$(RESET)" + @echo -e "$(BLUE)→ Removing vendor directory...$(RESET)" @rm -rf vendor @rm -f composer.lock - @echo "$(GREEN)✓ Deep clean complete$(RESET)" + @echo -e "$(GREEN)✓ Deep clean complete$(RESET)" # ============================================================================== # VALIDATION & SECURITY # ============================================================================== validate: ## Validate composer.json - @echo "$(BLUE)→ Validating composer.json...$(RESET)" + @echo -e "$(BLUE)→ Validating composer.json...$(RESET)" @if [ ! -f "$(COMPOSER_BIN)" ]; then \ - echo "$(RED)✗ Composer not found. Please install Composer first.$(RESET)"; \ - echo "$(YELLOW) Visit: https://getcomposer.org/download/$(RESET)"; \ + echo -e "$(RED)✗ Composer not found. Please install Composer first.$(RESET)"; \ + echo -e "$(YELLOW) Visit: https://getcomposer.org/download/$(RESET)"; \ exit 1; \ fi @$(COMPOSER) validate --strict --no-check-publish - @echo "$(GREEN)✓ composer.json is valid$(RESET)" + @echo -e "$(GREEN)✓ composer.json is valid$(RESET)" security: ## Check for security vulnerabilities - @echo "$(BLUE)→ Checking for security vulnerabilities...$(RESET)" + @echo -e "$(BLUE)→ Checking for security vulnerabilities...$(RESET)" @if $(COMPOSER) audit --no-dev --locked 2>&1 | grep -q "security vulnerability"; then \ - echo "$(RED)✗ Security vulnerabilities found$(RESET)"; \ + echo -e "$(RED)✗ Security vulnerabilities found$(RESET)"; \ $(COMPOSER) audit --no-dev --locked; \ exit 1; \ elif $(COMPOSER) audit --no-dev --locked 2>&1 | grep -q "abandoned"; then \ - echo "$(YELLOW)⚠ Found abandoned packages (informational only):$(RESET)"; \ + echo -e "$(YELLOW)⚠ Found abandoned packages (informational only):$(RESET)"; \ $(COMPOSER) audit --no-dev --locked || true; \ - echo "$(GREEN)✓ No security vulnerabilities found$(RESET)"; \ + echo -e "$(GREEN)✓ No security vulnerabilities found$(RESET)"; \ else \ - echo "$(GREEN)✓ No security vulnerabilities found$(RESET)"; \ + echo -e "$(GREEN)✓ No security vulnerabilities found$(RESET)"; \ fi security-strict: ## Check for security vulnerabilities (strict mode) - @echo "$(BLUE)→ Checking for security vulnerabilities (strict)...$(RESET)" + @echo -e "$(BLUE)→ Checking for security vulnerabilities (strict)...$(RESET)" @$(COMPOSER) audit - @echo "$(GREEN)✓ No security vulnerabilities or abandoned packages$(RESET)" + @echo -e "$(GREEN)✓ No security vulnerabilities or abandoned packages$(RESET)" outdated: ## Check for outdated dependencies - @echo "$(BLUE)→ Checking for outdated dependencies...$(RESET)" + @echo -e "$(BLUE)→ Checking for outdated dependencies...$(RESET)" @$(COMPOSER) outdated --direct diff --git a/Makefile b/Makefile index 31c28c1..6face23 100644 --- a/Makefile +++ b/Makefile @@ -61,8 +61,8 @@ endef help: ## Display this help message @echo "" - @echo "$(BOLD)$(CYAN)KaririCode\\DevKit - Development Makefile$(RESET)" - @echo "$(BLUE)═══════════════════════════════════════════════════════$(RESET)" + @echo -e "$(BOLD)$(CYAN)KaririCode\\DevKit - Development Makefile$(RESET)" + @echo -e "$(BLUE)═══════════════════════════════════════════════════════$(RESET)" @awk -v TITLE="🚀 Main Pipeline" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/pipeline/Makefile.orchestration.mk @awk -v TITLE="🛠️ Setup & Maintenance" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/local/Makefile.setup.mk @@ -72,14 +72,14 @@ help: ## Display this help message @awk -v TITLE="🐳 Docker Compose" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-compose.mk @awk -v TITLE="🐳 Docker Core" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-core.mk @awk -v TITLE="🐳 Docker Tools" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-tools.mk - @awk -v TITLE="🐳 Docker Image" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-image.mk + @awk -v TITLE="🐳 Docker Image" '$(AWK_HELP_SCRIPT)' $(MAKE_DIR)/docker/Makefile.docker-image.mk @echo "" - @echo "$(BOLD)Usage Examples:$(RESET)" - @echo " make $(CYAN)install$(RESET) # Install all dependencies" - @echo " make $(CYAN)ci$(RESET) # Run local CI pipeline" - @echo " make $(CYAN)docker-ci$(RESET) # Run CI in Docker (isolated)" - @echo " make $(CYAN)docker-shell$(RESET) # Open interactive Docker shell" + @echo -e "$(BOLD)Usage Examples:$(RESET)" + @echo -e " $(YELLOW)make install$(RESET) # Install all dependencies" + @echo -e " $(YELLOW)make ci$(RESET) # Run local CI pipeline" + @echo -e " $(YELLOW)make docker-ci$(RESET) # Run CI in Docker (isolated)" + @echo -e " $(YELLOW)make docker-shell$(RESET) # Open interactive Docker shell" @echo "" debug-composer: ## Debug composer configuration diff --git a/composer.json b/composer.json index acb63f4..3268faf 100644 --- a/composer.json +++ b/composer.json @@ -45,20 +45,6 @@ "phpmd/phpmd": "^2.15", "rector/rector": "2.2.4" }, - "scripts": { - "post-install-cmd": [ - "@php -r \"if (!file_exists('.env')) { copy('.env.example', '.env'); echo 'Created .env file from .env.example\\n'; }\"" - ], - "test": "echo 'DevKit base - no tests. Use install.sh to create a component.'", - "check-ready": [ - "@php -r \"echo '\\n🔍 Checking DevKit readiness...\\n\\n';\"", - "@php -r \"echo (version_compare(PHP_VERSION, '8.3.0') >= 0 ? '✓' : '✗') . ' PHP version: ' . PHP_VERSION . '\\n';\"", - "@php -r \"echo (extension_loaded('redis') ? '✓' : '⚠') . ' Redis extension\\n';\"", - "@php -r \"echo (extension_loaded('memcached') ? '✓' : '⚠') . ' Memcached extension\\n';\"", - "@php -r \"echo (extension_loaded('xdebug') ? '✓' : '⚠') . ' Xdebug extension\\n';\"", - "@php -r \"echo '\\n📦 DevKit is ready! Run ./install.sh to create a component.\\n\\n';\"" - ] - }, "scripts-descriptions": { "check-ready": "Verify DevKit environment is properly configured" }, diff --git a/docker-compose.yml b/docker-compose.yml index f104595..3c788e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,54 +1,423 @@ +# ============================================================================== +# KaririCode DevKit - Professional Docker Compose Configuration +# ============================================================================== +# Production-grade development environment following Docker best practices +# +# Features: +# - Resource limits and reservations +# - Health checks with proper timeouts +# - Security hardening (read-only mounts, tmpfs, capabilities) +# - Structured logging +# - Multi-environment profiles +# - Named volumes for performance +# - Proper dependency management +# +# Usage: +# Development: make up-safe +# Production: docker compose --profile production up +# Testing: docker compose --profile testing up +# +# Documentation: https://docs.docker.com/compose/compose-file/ +# ============================================================================== + +# Modern Compose syntax (no version needed for Compose v2+) +# Ref: https://docs.docker.com/compose/compose-file/04-version-and-name/ +name: ${APP_NAME:-kariricode-devkit} + services: + # ============================================================================ + # PHP-FPM + Nginx + Redis Service (kariricode/php-api-stack) + # ============================================================================ php: - image: kariricode/php-api-stack:dev + image: kariricode/php-api-stack:${PHP_STACK_VERSION:-dev} container_name: ${APP_NAME:-kariricode-devkit}_php + hostname: php-app + + # Restart policy for production stability + restart: unless-stopped + + # Working directory working_dir: /var/www/html + + # User and group for running the application (security best practice) + # NOTE: Commented out - image runs as root internally but processes run as www-data + # user: "${UID:-1000}:${GID:-1000}" + + # Resource limits (prevent runaway processes) + deploy: + resources: + limits: + cpus: "${PHP_CPU_LIMIT:-2.0}" + memory: ${PHP_MEMORY_LIMIT:-2G} + reservations: + cpus: "${PHP_CPU_RESERVATION:-0.5}" + memory: ${PHP_MEMORY_RESERVATION:-512M} + + # Port mappings + ports: + - target: 80 + published: ${APP_PORT:-8089} + protocol: tcp + mode: host + - target: 6379 + published: ${REDIS_PORT:-6379} + protocol: tcp + mode: host + env_file: - .env - ports: - # Expose the application port to the host - - "${APP_PORT:-8089}:80" - # Expose the internal redis port to the host if REDIS_HOST_PORT is set - - "${REDIS_PORT:-6379}:6379" + # Volume mounts volumes: - - ./:/var/www/html - - ./devkit/.config/php/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini:ro - - ./devkit/.config/php/error-reporting.ini:/usr/local/etc/php/conf.d/error-reporting.ini:ro - - ./devkit/.config/php/opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini:ro - environment: - APP_ENV: ${APP_ENV:-development} - APP_DEBUG: ${APP_DEBUG:-true} - DEMO_MODE: ${DEMO_MODE:-false} - HEALTH_CHECK_INSTALL: ${HEALTH_CHECK_INSTALL:-true} - COMPOSER_MEMORY_LIMIT: ${COMPOSER_MEMORY_LIMIT:--1} - XDEBUG_MODE: ${XDEBUG_MODE:-off} - XDEBUG_CONFIG: client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal} + # Application code (delegated for macOS performance) + - type: bind + source: ./ + target: /var/www/html + consistency: ${VOLUME_CONSISTENCY:-cached} + + # Named volumes for cache (performance optimization) + - composer-cache:/root/.composer/cache + - phpstan-cache:/var/www/html/var/cache/phpstan + - php-session:/var/lib/php/sessions + + # Temporary filesystem for uploads (security + performance) + - type: tmpfs + target: /tmp + tmpfs: + size: ${TMPFS_SIZE:-100M} + mode: 1777 + + # # Environment variables + # environment: + # # Application + # APP_ENV: ${APP_ENV:-development} + # APP_DEBUG: ${APP_DEBUG:-true} + # APP_SECRET: ${APP_SECRET:-change-me-in-production} + # APP_VERSION: ${APP_VERSION:-dev} + # SYMFONY_ENV: ${SYMFONY_ENV:-dev} + + # # Session Handler (CRITICAL) + # SESSION_SAVE_HANDLER: ${SESSION_SAVE_HANDLER:-files} + # SESSION_SAVE_PATH: ${SESSION_SAVE_PATH:-/tmp} + + # # Redis Configuration (Internal - inside container) + # REDIS_HOST: ${REDIS_HOST:-127.0.0.1} + # REDIS_PORT: ${REDIS_PORT_INTERNAL:-6379} + # REDIS_PASSWORD: ${REDIS_PASSWORD:-} + # REDIS_DB: ${REDIS_DB:-0} + # REDIS_TIMEOUT: ${REDIS_TIMEOUT:-5} + + # # Redis Server Configuration + # REDIS_LOGFILE: ${REDIS_LOGFILE:-/var/log/redis/redis.log} + # REDIS_LOGLEVEL: ${REDIS_LOGLEVEL:-notice} + # REDIS_MAXMEMORY: ${REDIS_MAXMEMORY:-256mb} + # REDIS_MAXMEMORY_POLICY: ${REDIS_MAXMEMORY_POLICY:-allkeys-lru} + # REDIS_SAVE: ${REDIS_SAVE:-} + # REDIS_APPENDONLY: ${REDIS_APPENDONLY:-no} + # REDIS_LOG_FILE: ${REDIS_LOG_FILE:-/var/log/redis/redis.log} + + # # Memcached Configuration (External service) + # MEMCACHED_HOST: memcached + # MEMCACHED_PORT: 11211 + + # # PHP Configuration + # PHP_MEMORY_LIMIT: ${PHP_MEMORY_LIMIT:-2G} + # PHP_MAX_EXECUTION_TIME: ${PHP_MAX_EXECUTION_TIME:-300} + # PHP_UPLOAD_MAX_FILESIZE: ${PHP_UPLOAD_MAX_FILESIZE:-50M} + # PHP_POST_MAX_SIZE: ${PHP_POST_MAX_SIZE:-50M} + + # # PHP-FPM Configuration + # PHP_FPM_PM: ${PHP_FPM_PM:-dynamic} + # PHP_FPM_PM_MAX_CHILDREN: ${PHP_FPM_PM_MAX_CHILDREN:-50} + # PHP_FPM_PM_START_SERVERS: ${PHP_FPM_PM_START_SERVERS:-5} + # PHP_FPM_PM_MIN_SPARE_SERVERS: ${PHP_FPM_PM_MIN_SPARE_SERVERS:-5} + # PHP_FPM_PM_MAX_SPARE_SERVERS: ${PHP_FPM_PM_MAX_SPARE_SERVERS:-10} + # PHP_FPM_PM_MAX_REQUESTS: ${PHP_FPM_PM_MAX_REQUESTS:-500} + + # # Nginx Configuration + # NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} + # NGINX_WORKER_CONNECTIONS: ${NGINX_WORKER_CONNECTIONS:-1024} + # NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M} + # NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65} + + # # Composer + # COMPOSER_MEMORY_LIMIT: ${COMPOSER_MEMORY_LIMIT:--1} + # COMPOSER_HOME: ${COMPOSER_HOME:-/root/.composer} + + # # Xdebug + # XDEBUG_MODE: ${XDEBUG_MODE:-off} + # XDEBUG_CLIENT_HOST: ${XDEBUG_CLIENT_HOST:-host.docker.internal} + # XDEBUG_CLIENT_PORT: ${XDEBUG_CLIENT_PORT:-9003} + # XDEBUG_SESSION: ${XDEBUG_SESSION:-PHPSTORM} + + # # OPcache + # OPCACHE_ENABLE: ${OPCACHE_ENABLE:-1} + # OPCACHE_VALIDATE_TIMESTAMPS: ${OPCACHE_VALIDATE_TIMESTAMPS:-1} + # OPCACHE_REVALIDATE_FREQ: ${OPCACHE_REVALIDATE_FREQ:-2} + + # # Features + # DEMO_MODE: ${DEMO_MODE:-false} + # HEALTH_CHECK_INSTALL: ${HEALTH_CHECK_INSTALL:-true} + + # # Timezone + # TZ: ${TZ:-UTC} + + # # Logging + # LOG_LEVEL: ${LOG_LEVEL:-info} + + # Network configuration networks: - - kariricode_network + kariricode_network: + aliases: + - php-service + - api-server + + # DNS configuration + dns: + - 8.8.8.8 + - 8.8.4.4 + + # Extra hosts for host machine communication extra_hosts: - - host.docker.internal:host-gateway + - "host.docker.internal:host-gateway" + + # Health check + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"] + interval: ${HEALTHCHECK_INTERVAL:-30s} + timeout: ${HEALTHCHECK_TIMEOUT:-10s} + retries: ${HEALTHCHECK_RETRIES:-3} + start_period: ${HEALTHCHECK_START_PERIOD:-40s} + + # Logging configuration + logging: + driver: "json-file" + options: + max-size: "${LOG_MAX_SIZE:-10m}" + max-file: "${LOG_MAX_FILE:-3}" + compress: "true" + labels: "service,environment" + + # Labels for organization and monitoring + labels: + com.kariricode.service: "php-api" + com.kariricode.environment: "${APP_ENV:-development}" + com.kariricode.version: "${APP_VERSION:-dev}" + com.kariricode.description: "PHP-FPM + Nginx + Redis service (kariricode/php-api-stack)" + com.kariricode.maintainer: "devops@kariricode.org" + + # Dependencies with health check conditions depends_on: memcached: condition: service_healthy + restart: true + + # Security options + security_opt: + - no-new-privileges:true + # Capability management (drop all, add only needed) + cap_drop: + - ALL + cap_add: + - CHOWN + - SETGID + - SETUID + - NET_BIND_SERVICE + - DAC_OVERRIDE + + # Profiles for different environments + profiles: + - development + - production + - testing + + # ============================================================================ + # Memcached Service + # ============================================================================ memcached: - image: memcached:1.6-alpine + image: memcached:${MEMCACHED_VERSION:-1.6-alpine} container_name: ${APP_NAME:-kariricode-devkit}_memcached - command: memcached -m ${MEMCACHED_MEMORY:-256} - env_file: - - .env + hostname: memcached-cache + + # Restart policy + restart: unless-stopped + + # Command with tuned parameters + command: > + memcached + -m ${MEMCACHED_MEMORY:-256} + -c ${MEMCACHED_MAX_CONNECTIONS:-1024} + -t ${MEMCACHED_THREADS:-4} + -I ${MEMCACHED_MAX_ITEM_SIZE:-5m} + -v + + # Resource limits + deploy: + resources: + limits: + cpus: "${MEMCACHED_CPU_LIMIT:-1.0}" + memory: ${MEMCACHED_MEMORY_TOTAL:-512M} + reservations: + cpus: "${MEMCACHED_CPU_RESERVATION:-0.25}" + memory: ${MEMCACHED_MEMORY_RESERVATION:-256M} + + # Port mapping ports: - - "${MEMCACHED_PORT:-11211}:11211" + - target: 11211 + published: ${MEMCACHED_PORT:-11211} + protocol: tcp + mode: host + + # Network networks: - - kariricode_network + kariricode_network: + aliases: + - cache-service + + # Health check with proper validation healthcheck: test: ["CMD", "nc", "-z", "127.0.0.1", "11211"] - interval: 10s - timeout: 3s - retries: 3 - start_period: 5s + interval: ${MEMCACHED_HEALTHCHECK_INTERVAL:-10s} + timeout: ${MEMCACHED_HEALTHCHECK_TIMEOUT:-5s} + retries: ${MEMCACHED_HEALTHCHECK_RETRIES:-3} + start_period: ${MEMCACHED_HEALTHCHECK_START_PERIOD:-10s} + + # Logging + logging: + driver: "json-file" + options: + max-size: "${LOG_MAX_SIZE:-10m}" + max-file: "${LOG_MAX_FILE:-3}" + compress: "true" + # Labels + labels: + com.kariricode.service: "memcached" + com.kariricode.environment: "${APP_ENV:-development}" + com.kariricode.description: "Memcached caching service" + com.kariricode.maintainer: "devops@kariricode.org" + + # Security + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - SETGID + - SETUID + + # User (non-root) + user: "memcache" + + # Read-only root filesystem (security hardening) + read_only: true + tmpfs: + - /tmp:size=50M,mode=1777 + + # Profiles + profiles: + - development + - production + - testing + +# ============================================================================== +# Networks +# ============================================================================== networks: kariricode_network: driver: bridge name: ${APP_NAME:-kariricode-devkit}_network + + # Enable IPv6 if needed + enable_ipv6: ${ENABLE_IPV6:-false} + + # Network configuration + driver_opts: + com.docker.network.bridge.name: ${BRIDGE_NAME:-kariricode0} + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.driver.mtu: ${NETWORK_MTU:-1500} + + # IPAM configuration + ipam: + driver: default + config: + - subnet: ${NETWORK_SUBNET:-172.20.0.0/16} + gateway: ${NETWORK_GATEWAY:-172.20.0.1} + + # Labels + labels: + com.kariricode.network: "main" + com.kariricode.environment: "${APP_ENV:-development}" + +# ============================================================================== +# Volumes +# ============================================================================== +volumes: + # Composer cache for faster dependency installation + composer-cache: + name: ${APP_NAME:-kariricode-devkit}_composer_cache + driver: local + labels: + com.kariricode.volume: "composer-cache" + com.kariricode.description: "Composer dependencies cache" + + # PHPStan cache for faster static analysis + phpstan-cache: + name: ${APP_NAME:-kariricode-devkit}_phpstan_cache + driver: local + labels: + com.kariricode.volume: "phpstan-cache" + com.kariricode.description: "PHPStan analysis cache" + + # PHP session storage + php-session: + name: ${APP_NAME:-kariricode-devkit}_php_session + driver: local + labels: + com.kariricode.volume: "php-session" + com.kariricode.description: "PHP session data" + +# ============================================================================== +# Extension Fields (DRY - reusable configurations) +# ============================================================================== +x-logging: &default-logging + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + compress: "true" + +x-healthcheck-defaults: &healthcheck-defaults + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +x-security-defaults: &security-defaults + security_opt: + - no-new-privileges:true + cap_drop: + - ALL +# ============================================================================== +# Profiles Documentation +# ============================================================================== +# +# Available profiles: +# - development: Full dev stack with Xdebug and verbose logging +# - production: Optimized for production with minimal logging +# - testing: Isolated testing environment +# +# Usage examples: +# make up-safe # Development (recommended) +# docker compose --profile development up +# docker compose --profile production up -d +# docker compose --profile testing run php vendor/bin/phpunit +# +# Troubleshooting: +# make diagnose-ports # Check port conflicts +# make status # Service status +# make logs SERVICE=php # View logs +# make shell # Enter PHP container +# +# ============================================================================== diff --git a/install.sh b/install.sh deleted file mode 100644 index c9bd478..0000000 --- a/install.sh +++ /dev/null @@ -1,638 +0,0 @@ -#!/bin/bash - -################################################################################ -# KaririCode DevKit - Professional Component Installer -################################################################################ -# This script automates the setup of a new KaririCode Framework component -# with a fully configured development environment. -# -# Usage: ./install.sh -################################################################################ - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Emoji support -CHECK="✓" -CROSS="✗" -ARROW="→" -ROCKET="🚀" -PACKAGE="📦" -WRENCH="🔧" -TEST="🧪" -CLEAN="🧹" - -################################################################################ -# UTILITY FUNCTIONS -################################################################################ - -print_header() { - echo "" - echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" - echo -e "${BLUE}║ KaririCode DevKit - Component Installation Wizard ║${NC}" - echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}" - echo "" -} - -print_step() { - echo -e "${BLUE}${ARROW}${NC} $1" -} - -print_success() { - echo -e "${GREEN}${CHECK}${NC} $1" -} - -print_error() { - echo -e "${RED}${CROSS}${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}!${NC} $1" -} - -prompt_input() { - local prompt="$1" - local var_name="$2" - local default="$3" - - if [ -n "$default" ]; then - read -p "$(echo -e ${BLUE}${prompt}${NC} [${default}]: )" input - eval $var_name="${input:-$default}" - else - read -p "$(echo -e ${BLUE}${prompt}${NC}: )" input - eval $var_name="$input" - fi -} - -prompt_confirm() { - local prompt="$1" - read -p "$(echo -e ${YELLOW}${prompt}${NC} [y/N]: )" -r - echo - [[ $REPLY =~ ^[Yy]$ ]] -} - -validate_namespace() { - local namespace="$1" - if [[ ! "$namespace" =~ ^[A-Z][a-zA-Z0-9]*$ ]]; then - return 1 - fi - return 0 -} - -################################################################################ -# MAIN INSTALLATION STEPS -################################################################################ - -collect_information() { - print_step "Collecting component information..." - echo "" - - # Component Name - while true; do - prompt_input "Component name (e.g., Cache, Validator, Router)" COMPONENT_NAME - - if [ -z "$COMPONENT_NAME" ]; then - print_error "Component name cannot be empty" - continue - fi - - if ! validate_namespace "$COMPONENT_NAME"; then - print_error "Name must start with uppercase letter and contain only letters and numbers" - continue - fi - - break - done - - # Convert to lowercase for package name - PACKAGE_NAME=$(echo "$COMPONENT_NAME" | sed 's/\([A-Z]\)/-\1/g' | sed 's/^-//' | tr '[:upper:]' '[:lower:]') - - # Description - prompt_input "Component description" COMPONENT_DESCRIPTION "Professional ${COMPONENT_NAME} implementation for KaririCode Framework" - - # Author information - prompt_input "Author name" AUTHOR_NAME "Walmir Silva" - prompt_input "Author email" AUTHOR_EMAIL "walmir.silva@kariricode.org" - - # PHP Version - prompt_input "Minimum PHP version" PHP_VERSION "8.4" - - echo "" - print_success "Information collected successfully!" - echo "" - echo -e "${BLUE}Component:${NC} $COMPONENT_NAME" - echo -e "${BLUE}Package:${NC} kariricode/$PACKAGE_NAME" - echo -e "${BLUE}Namespace:${NC} KaririCode\\$COMPONENT_NAME" - echo -e "${BLUE}Description:${NC} $COMPONENT_DESCRIPTION" - echo "" - - if ! prompt_confirm "Confirm the information above?"; then - print_error "Installation cancelled by user" - exit 0 - fi -} - -create_directory_structure() { - print_step "Creating directory structure..." - - mkdir -p src/{Adapter,Contract,Exception,Factory} - print_success "Created: src/ with subdirectories" - - mkdir -p tests/{Unit,Integration,Fixtures} - print_success "Created: tests/ with subdirectories" - - mkdir -p docs - print_success "Created: docs/" - - # Create .gitkeep files - touch src/.gitkeep - touch tests/Unit/.gitkeep - touch tests/Integration/.gitkeep - touch tests/Fixtures/.gitkeep - - print_success "Directory structure created" -} - -generate_composer_json() { - print_step "Generating composer.json..." - - cat > composer.json < README.md < .env < /dev/null; then - print_error "Docker is not installed" - return 1 - fi - - if ! command -v docker-compose &> /dev/null; then - print_error "Docker Compose is not installed" - return 1 - fi - - docker-compose up -d - - # Wait for containers to be ready - print_step "Waiting for containers to be ready..." - sleep 5 - - print_success "Docker environment started" -} - -install_dependencies() { - print_step "Installing Composer dependencies..." - - docker-compose exec -T php composer install --no-interaction --prefer-dist - - print_success "Dependencies installed" -} - -run_tests() { - print_step "Running environment tests..." - - # Test PHP version - print_step "Checking PHP version..." - docker-compose exec -T php php -v - - # Test Composer - print_step "Checking Composer..." - docker-compose exec -T php composer --version - - # Test autoload - print_step "Testing autoload..." - docker-compose exec -T php composer dump-autoload - - print_success "Environment tests completed" -} - -run_quality_checks() { - print_step "Running quality checks..." - - # Validate composer.json - print_step "Validating composer.json..." - docker-compose exec -T php composer validate --strict - - # Check PHP syntax - print_step "Checking PHP syntax..." - docker-compose exec -T php find src -name "*.php" -print0 | xargs -0 -n1 php -l - - print_success "Quality checks completed" -} - -run_installation_checklist() { - print_step "Running installation checklist..." - echo "" - - local all_passed=true - - # Check directories - echo -e "${BLUE}Checking directory structure:${NC}" - for dir in src tests docs devkit; do - if [ -d "$dir" ]; then - print_success "Directory $dir exists" - else - print_error "Directory $dir not found" - all_passed=false - fi - done - echo "" - - # Check configuration files - echo -e "${BLUE}Checking configuration files:${NC}" - for file in composer.json docker-compose.yml Makefile phpunit.xml phpstan.neon .php-cs-fixer.php; do - if [ -f "$file" ]; then - print_success "File $file exists" - else - print_error "File $file not found" - all_passed=false - fi - done - echo "" - - # Check Docker containers - echo -e "${BLUE}Checking Docker containers:${NC}" - if docker-compose ps | grep -q "Up"; then - print_success "Containers are running" - else - print_error "Containers are not running" - all_passed=false - fi - echo "" - - # Check vendor directory - echo -e "${BLUE}Checking dependencies:${NC}" - if [ -d "vendor" ]; then - print_success "Dependencies installed" - else - print_error "Dependencies not installed" - all_passed=false - fi - echo "" - - if $all_passed; then - print_success "All checks passed!" - return 0 - else - print_error "Some checks failed" - return 1 - fi -} - -cleanup_installation_files() { - print_step "Removing installation files..." - - # Remove installation files - rm -f install.sh - print_success "Removed: install.sh" - - # Remove DevKit .git if exists - if [ -d ".git" ]; then - rm -rf .git - print_success "Removed: .git (DevKit history)" - fi - - # Remove template files - rm -f .github/workflows/devkit-*.yml 2>/dev/null || true - - print_success "Installation files removed" -} - -initialize_git_repository() { - print_step "Initializing Git repository..." - - git init - git add . - git commit -m "feat: initial commit - KaririCode ${COMPONENT_NAME} component - -- Setup development environment -- Configure quality tools -- Add basic project structure - -Generated by KaririCode DevKit" - - print_success "Git repository initialized" -} - -display_next_steps() { - echo "" - echo -e "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}" - echo -e "${GREEN}║ Installation Completed Successfully! ${ROCKET} ║${NC}" - echo -e "${GREEN}╚════════════════════════════════════════════════════════════════╝${NC}" - echo "" - echo -e "${BLUE}${PACKAGE} Component:${NC} KaririCode ${COMPONENT_NAME}" - echo -e "${BLUE}${PACKAGE} Namespace:${NC} KaririCode\\${COMPONENT_NAME}" - echo -e "${BLUE}${PACKAGE} Package:${NC} kariricode/${PACKAGE_NAME}" - echo "" - echo -e "${YELLOW}Next steps:${NC}" - echo "" - echo -e " ${ARROW} Access container shell:" - echo -e " ${GREEN}make shell${NC}" - echo "" - echo -e " ${ARROW} Create your classes in ${BLUE}src/${NC}" - echo -e " ${GREEN}src/Contract/${COMPONENT_NAME}Interface.php${NC}" - echo -e " ${GREEN}src/${COMPONENT_NAME}.php${NC}" - echo "" - echo -e " ${ARROW} Write tests in ${BLUE}tests/${NC}" - echo -e " ${GREEN}tests/Unit/${COMPONENT_NAME}Test.php${NC}" - echo "" - echo -e " ${ARROW} Run tests:" - echo -e " ${GREEN}make test${NC}" - echo "" - echo -e " ${ARROW} Check code quality:" - echo -e " ${GREEN}make qa${NC}" - echo "" - echo -e " ${ARROW} See all available commands:" - echo -e " ${GREEN}make help${NC}" - echo "" - echo -e "${BLUE}Documentation:${NC} https://kariricode.org/docs" - echo -e "${BLUE}GitHub:${NC} https://github.com/KaririCode-Framework" - echo "" - echo -e "${GREEN}Happy coding! ${ROCKET}${NC}" - echo "" -} - -################################################################################ -# MAIN EXECUTION -################################################################################ - -main() { - clear - print_header - - # Step 1: Collect information - collect_information - - # Step 2: Create directory structure - create_directory_structure - - # Step 3: Generate composer.json - generate_composer_json - - # Step 4: Generate README - generate_readme - - # Step 5: Update .env - update_env_file - - # Step 6: Start Docker environment - if ! start_docker_environment; then - print_error "Falha ao iniciar ambiente Docker" - exit 1 - fi - - # Step 7: Install dependencies - install_dependencies - - # Step 8: Run tests - run_tests - - # Step 9: Run quality checks - run_quality_checks - - # Step 10: Run checklist - if ! run_installation_checklist; then - print_warning "Some checks failed, but installation can continue" - fi - - # Step 11: Cleanup - cleanup_installation_files - - # Step 12: Initialize Git - initialize_git_repository - - # Step 13: Display next steps - display_next_steps -} - -# Run main function -main "$@" \ No newline at end of file From 55629c9fe833f3ee4fbb4308dc744bff726c49bb Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Mon, 10 Nov 2025 16:08:25 -0300 Subject: [PATCH 7/7] docs(docker): fix typos and formatting in MAKEFILE-docker.md --- .docs/MAKEFILE-docker.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.docs/MAKEFILE-docker.md b/.docs/MAKEFILE-docker.md index ea80664..dba34ce 100644 --- a/.docs/MAKEFILE-docker.md +++ b/.docs/MAKEFILE-docker.md @@ -39,14 +39,19 @@ The Docker modules provide containerized execution for: - **Clean State**: No local configuration interference ### Module Architecture -``` -Docker Modules (.make/docker/) +```tree +. +├── .make/docker/ +│ ├── Makefile.docker-core.mk # → Core: Shell, Composer, PHP execution +│ ├── Makefile.docker-compose.mk # → Stack: Full stack management (up, down, logs) +│ ├── Makefile.docker-qa.mk # → QA: CI/CD pipeline in containers +│ ├── Makefile.docker-image.mk # → Image: Pull, info, clean +│ └── Makefile.docker-tools.mk # → Tools: htop, jq, vim, network diagnostics │ -├── Makefile.docker-core.mk → Shell, Composer, PHP execution -├── Makefile.docker-compose.mk → Full stack management (see MAKEFILE-compose.md) -├── Makefile.docker-qa.mk → QA pipeline in containers -├── Makefile.docker-image.mk → Image management -└── Makefile.docker-tools.mk → Utility tools +└── arfa-runtime-stack/docker/ + ├── entrypoint.sh # → Entrypoint: Container initialization script + ├── config-processor.sh # → Config: Processes PHP/Swoole configs from env vars + └── health-check.sh # → Health: Docker health check script ``` ### Docker vs Local Execution