# CI/CD Extension Validation Guide

**Last Updated:** 2026-01-15

## Overview

This guide explains how to integrate PHP extension validation into CI/CD pipelines to catch missing extensions before code reaches production.

## Integration Benefits

1. **Early Detection** - Catch missing extensions in CI/CD
2. **Automated Validation** - No manual checks required
3. **Consistent Testing** - Same checks across all environments
4. **Prevent Production Failures** - Block deployments with missing extensions

## GitHub Actions

### Basic Workflow

**File:** `.github/workflows/php-extensions.yml`

```yaml
name: PHP Extension Validation

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  validate-extensions:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.0'
          extensions: mbstring, iconv, curl, json, gd, zip, xml
      
      - name: Validate extensions
        run: php v2/scripts/dev-helpers/check-php-extensions.php
      
      - name: Pre-deployment check
        run: php v2/scripts/dev-helpers/pre-deployment-check.php
      
      - name: Test fallbacks
        run: php v2/scripts/dev-helpers/test-extension-fallbacks.php
      
      - name: Run unit tests
        run: php tests/php-extensions/ExtensionFallbackTest.php
```

### Advanced Workflow with Matrix

**File:** `.github/workflows/php-extensions-matrix.yml`

```yaml
name: PHP Extension Validation (Matrix)

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  validate-extensions:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        php-version: ['8.0', '8.1', '8.2']
        extensions:
          - mbstring,iconv,curl,json
          - mbstring,iconv,curl,json,gd,zip,xml
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup PHP ${{ matrix.php-version }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-version }}
          extensions: ${{ matrix.extensions }}
      
      - name: Validate extensions
        run: php v2/scripts/dev-helpers/check-php-extensions.php
      
      - name: Test fallbacks
        run: php v2/scripts/dev-helpers/test-extension-fallbacks.php
```

### Composer Integration

**File:** `.github/workflows/composer-extensions.yml`

```yaml
name: Composer Extension Validation

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  validate-composer:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.0'
          extensions: mbstring, iconv, curl, json
      
      - name: Validate composer.json
        run: composer validate
      
      - name: Check extension requirements
        run: |
          if ! grep -q "ext-mbstring\|ext-iconv\|ext-json\|ext-curl" composer.json; then
            echo "❌ Extension requirements not declared in composer.json"
            exit 1
          fi
          echo "✅ Extension requirements declared"
      
      - name: Test composer install
        run: composer install --no-dev --no-interaction
```

## GitLab CI

### Basic Pipeline

**File:** `.gitlab-ci.yml` (add to existing)

```yaml
validate_extensions:
  image: php:8.0
  before_script:
    - apt-get update && apt-get install -y php-mbstring php-iconv php-curl php-gd php-zip php-xml
  script:
    - php v2/scripts/dev-helpers/check-php-extensions.php
    - php v2/scripts/dev-helpers/pre-deployment-check.php
    - php v2/scripts/dev-helpers/test-extension-fallbacks.php
    - php tests/php-extensions/ExtensionFallbackTest.php
  only:
    - main
    - develop
    - merge_requests
```

### Multi-Stage Pipeline

**File:** `.gitlab-ci.yml` (complete example)

```yaml
stages:
  - validate
  - test
  - deploy

validate_extensions:
  stage: validate
  image: php:8.0
  before_script:
    - apt-get update && apt-get install -y php-mbstring php-iconv php-curl php-gd php-zip php-xml
  script:
    - php v2/scripts/dev-helpers/check-php-extensions.php
    - php v2/scripts/dev-helpers/pre-deployment-check.php
  artifacts:
    reports:
      junit: extension-validation.xml
  only:
    - main
    - develop
    - merge_requests

test_fallbacks:
  stage: test
  image: php:8.0
  before_script:
    - apt-get update && apt-get install -y php-mbstring php-iconv php-curl
  script:
    - php v2/scripts/dev-helpers/test-extension-fallbacks.php
    - php tests/php-extensions/ExtensionFallbackTest.php
  only:
    - main
    - develop
    - merge_requests
```

## Pre-Commit Hooks

### Git Pre-Commit Hook

**File:** `.git/hooks/pre-commit`

```bash
#!/bin/bash
#
# Pre-commit hook to validate PHP extensions
#

PROJECT_ROOT="$(git rev-parse --show-toplevel)"

# Check if PHP files changed
CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$')

if [ -z "$CHANGED_FILES" ]; then
    exit 0
fi

echo "Running PHP extension validation..."

# Run extension check
if ! php "$PROJECT_ROOT/v2/scripts/dev-helpers/check-php-extensions.php" > /dev/null 2>&1; then
    echo "❌ PHP extension validation failed"
    echo "Run: php v2/scripts/dev-helpers/check-php-extensions.php"
    exit 1
fi

# Check for problematic patterns
PROBLEMATIC_PATTERNS=0

# Check for mb_* without checks
if echo "$CHANGED_FILES" | xargs grep -l "mb_[a-z_]*(" | xargs grep -v "function_exists\|extension_loaded\|_has_mbstring\|safe_mb_" > /dev/null 2>&1; then
    echo "⚠️  Found mb_* calls without checks"
    PROBLEMATIC_PATTERNS=$((PROBLEMATIC_PATTERNS + 1))
fi

# Check for curl_* without checks
if echo "$CHANGED_FILES" | xargs grep -l "curl_[a-z_]*(" | xargs grep -v "function_exists\|extension_loaded" > /dev/null 2>&1; then
    echo "⚠️  Found curl_* calls without checks"
    PROBLEMATIC_PATTERNS=$((PROBLEMATIC_PATTERNS + 1))
fi

if [ $PROBLEMATIC_PATTERNS -gt 0 ]; then
    echo "⚠️  Found {$PROBLEMATIC_PATTERNS} file(s) with extension calls without checks"
    echo "Consider adding function_exists() or extension_loaded() checks"
    # Don't fail commit, just warn
fi

echo "✅ Extension validation passed"
exit 0
```

**Make executable:**

```bash
chmod +x .git/hooks/pre-commit
```

## Jenkins

### Jenkinsfile

**File:** `Jenkinsfile`

```groovy
pipeline {
    agent any
    
    stages {
        stage('Validate Extensions') {
            steps {
                sh '''
                    php v2/scripts/dev-helpers/check-php-extensions.php
                    php v2/scripts/dev-helpers/pre-deployment-check.php
                '''
            }
        }
        
        stage('Test Fallbacks') {
            steps {
                sh 'php v2/scripts/dev-helpers/test-extension-fallbacks.php'
            }
        }
        
        stage('Unit Tests') {
            steps {
                sh 'php tests/php-extensions/ExtensionFallbackTest.php'
            }
        }
    }
    
    post {
        always {
            archiveArtifacts artifacts: '**/extension-validation.log', allowEmptyArchive: true
        }
        failure {
            emailext (
                subject: "Extension Validation Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
                body: "Extension validation failed. Check build logs.",
                to: "hady@ordio.com"
            )
        }
    }
}
```

## CircleCI

### Config File

**File:** `.circleci/config.yml`

```yaml
version: 2.1

jobs:
  validate-extensions:
    docker:
      - image: php:8.0
    steps:
      - checkout
      - run:
          name: Install extensions
          command: |
            apt-get update
            apt-get install -y php-mbstring php-iconv php-curl php-gd php-zip php-xml
      - run:
          name: Validate extensions
          command: php v2/scripts/dev-helpers/check-php-extensions.php
      - run:
          name: Pre-deployment check
          command: php v2/scripts/dev-helpers/pre-deployment-check.php
      - run:
          name: Test fallbacks
          command: php v2/scripts/dev-helpers/test-extension-fallbacks.php

workflows:
  version: 2
  validate:
    jobs:
      - validate-extensions
```

## Best Practices

### 1. Fail Fast

**Fail pipeline if extensions missing:**

```yaml
- name: Validate extensions
  run: php v2/scripts/dev-helpers/check-php-extensions.php
  # Exit code 1 will fail the pipeline
```

### 2. Cache Extension Checks

**Cache extension validation results:**

```yaml
- name: Cache extension check
  uses: actions/cache@v3
  with:
    path: /tmp/extension_check_cache
    key: ${{ runner.os }}-extensions-${{ hashFiles('composer.json') }}
```

### 3. Matrix Testing

**Test with different extension combinations:**

```yaml
strategy:
  matrix:
    extensions:
      - mbstring,iconv,curl,json
      - mbstring,iconv,curl,json,gd
      - mbstring,iconv,curl,json,gd,zip,xml
```

### 4. Parallel Execution

**Run checks in parallel:**

```yaml
jobs:
  validate-extensions:
    # Extension check
  test-fallbacks:
    # Fallback tests
  unit-tests:
    # Unit tests
```

## Notification Setup

### Email Notifications

**On failure:**

```yaml
- name: Notify on failure
  if: failure()
  run: |
    echo "Extension validation failed" | mail -s "CI Failure" hady@ordio.com
```

### Slack Notifications

**On failure:**

```yaml
- name: Slack notification
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    text: "Extension validation failed"
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
```

## Monitoring Integration

### Track Extension Status

**Log extension status:**

```yaml
- name: Log extension status
  run: |
    php v2/scripts/dev-helpers/check-php-extensions.php --json > extension_status.json
    # Upload to monitoring system
```

## Related Documentation

- `docs/development/PRODUCTION_DEPLOYMENT_CHECKLIST.md` - Deployment checklist
- `docs/development/PHP_EXTENSION_TESTING.md` - Testing procedures
- `v2/scripts/dev-helpers/check-php-extensions.php` - Extension validator
- `.cursor/rules/php-extensions.mdc` - Cursor rules

## Summary

1. **Integrate validation** into CI/CD pipeline
2. **Fail fast** if extensions missing
3. **Test fallbacks** in CI/CD
4. **Notify on failures** for quick response
5. **Monitor extension status** over time

**Remember**: CI/CD validation catches issues before they reach production.
