# GitLab CI for Laravel 12 API workflow: rules: - if: '$CI_PIPELINE_SOURCE == "push"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_TAG' stages: - validate - test - build - security - deploy variables: APP_ENV: testing APP_DEBUG: "false" LOG_CHANNEL: stderr CACHE_DRIVER: array SESSION_DRIVER: array QUEUE_CONNECTION: sync DB_CONNECTION: sqlite DB_DATABASE: "$CI_PROJECT_DIR/database/database.sqlite" COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.composer-cache" NPM_CONFIG_CACHE: "$CI_PROJECT_DIR/.npm-cache" cache: key: files: - composer.lock - package-lock.json paths: - .composer-cache/ - .npm-cache/ - vendor/ - node_modules/ .php_laravel_job: image: php:8.3-cli before_script: - apt-get update - apt-get install -y --no-install-recommends git unzip curl libzip-dev libpng-dev libonig-dev libxml2-dev libsqlite3-dev - docker-php-ext-install mbstring zip gd pdo_sqlite - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - php composer-setup.php --install-dir=/usr/local/bin --filename=composer - rm composer-setup.php - composer install --prefer-dist --no-interaction --no-progress # Install Pint if not present - composer require --dev laravel/pint --ignore-platform-reqs || true # Generate .env with real keys - | cat > .env <&1 | tee reports/npm-build.log artifacts: when: always expire_in: 7 days paths: - public/build/ - public/css/ - public/js/ - reports/ cache: paths: - .npm/ composer_audit: extends: .php_laravel_job stage: security script: - mkdir -p reports - composer audit --locked --no-interaction --format=json | tee reports/composer-audit.json artifacts: when: always expire_in: 7 days paths: - reports/ - storage/logs/ allow_failure: true npm_audit: image: node:22-alpine stage: security before_script: - npm ci script: - mkdir -p reports - npm audit --json --audit-level=high | tee reports/npm-audit.json || true artifacts: when: always expire_in: 7 days paths: - reports/ allow_failure: true secret_detection_basic: image: alpine:3.20 stage: security before_script: - apk add --no-cache git grep script: - mkdir -p reports - | echo "Scanning for committed secrets..." | tee reports/secret-detection.log if grep -RInE "(APP_KEY=base64:[A-Za-z0-9+/=]{44}|DB_PASSWORD=.+|JWT_SECRET=.+|MAIL_PASSWORD=.+|PAYPAL_(CLIENT|SECRET|MODE)|AKIA[0-9A-Z]{16}|-----BEGIN (RSA|OPENSSH|PRIVATE) KEY-----)" \ --exclude-dir=.git \ --exclude-dir=vendor \ --exclude-dir=node_modules \ --exclude=.env.example \ --exclude=.env.prod.example \ --exclude=reports \ . 2>&1 | tee -a reports/secret-detection.log; then echo "❌ Secrets detected! Check reports/secret-detection.log" exit 1 else echo "✅ No secrets found" fi artifacts: when: always expire_in: 7 days paths: - reports/ allow_failure: false # Deploy to shared hosting (optional - add if needed) deploy: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client sshpass rsync script: - echo "Deploying to production..." - sshpass -p "$SSH_PASSWORD" rsync -avz --delete \ -e "ssh -p $SSH_PORT -o StrictHostKeyChecking=no" \ --exclude='.env' \ --exclude='storage/logs/*' \ --exclude='.git' \ --exclude='tests' \ --exclude='database/sqlite' \ ./ $SSH_USER@$SSH_HOST:$SSH_TARGET_DIR/ environment: name: production url: $PRODUCTION_URL only: - main - master when: manual needs: - phpunit - composer_audit # Include GitLab native templates (ensure you have license) include: - template: Jobs/SAST.gitlab-ci.yml - template: Jobs/Secret-Detection.gitlab-ci.yml - template: Jobs/Dependency-Scanning.gitlab-ci.yml # Optional: Run SAST only on main branch sast: rules: - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "master"'