# GitLab CI for Laravel 12
# Save this file as: .gitlab-ci.yml

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_TAG'

stages:
  - validate
  - test
  - build
  - security

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
    - 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
    - |
      cat > .env <<EOF
      APP_NAME=school_api
      APP_ENV=testing
      APP_KEY=
      APP_DEBUG=false
      APP_URL=http://localhost

      LOG_CHANNEL=stderr
      LOG_LEVEL=debug

      DB_CONNECTION=sqlite
      DB_DATABASE=${CI_PROJECT_DIR}/database/database.sqlite

      CACHE_DRIVER=array
      SESSION_DRIVER=array
      QUEUE_CONNECTION=sync
      MAIL_MAILER=array
      BROADCAST_CONNECTION=log
      FILESYSTEM_DISK=local

      JWT_SECRET=ci_dummy_secret_not_for_production
      EOF
    - mkdir -p database storage/framework/cache storage/framework/sessions storage/framework/views storage/logs bootstrap/cache reports
    - touch database/database.sqlite
    - php artisan key:generate --force
    - php artisan config:clear

composer_validate:
  extends: .php_laravel_job
  stage: validate
  script:
    - mkdir -p reports
    - bash -o pipefail -c 'composer validate --strict 2>&1 | tee reports/composer-validate.log'
    - bash -o pipefail -c 'php -v 2>&1 | tee reports/php-version.log'
    - bash -o pipefail -c 'php artisan --version 2>&1 | tee reports/artisan-version.log'
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - reports/
      - storage/logs/

laravel_pint:
  extends: .php_laravel_job
  stage: validate
  script:
    - mkdir -p reports
    - bash -o pipefail -c 'vendor/bin/pint --test 2>&1 | tee reports/pint.log'
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - reports/
      - storage/logs/

phpunit:
  extends: .php_laravel_job
  stage: test
  script:
    - mkdir -p reports
    - bash -o pipefail -c 'php artisan migrate --force 2>&1 | tee reports/migrate.log'
    - bash -o pipefail -c 'php artisan test --testsuite=Feature --log-junit reports/phpunit-feature.xml 2>&1 | tee reports/phpunit-feature.log'
    - bash -o pipefail -c 'php artisan test --testsuite=Unit --log-junit reports/phpunit-unit.xml 2>&1 | tee reports/phpunit-unit.log'
  artifacts:
    when: always
    expire_in: 7 days
    reports:
      junit:
        - reports/phpunit-feature.xml
        - reports/phpunit-unit.xml
    paths:
      - reports/
      - storage/logs/

build_frontend:
  image: node:22-alpine
  stage: build
  before_script:
    - npm ci
  script:
    - mkdir -p reports
    - sh -c 'npm run build 2>&1 | tee reports/npm-build.log'
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - public/build/
      - reports/

composer_audit:
  extends: .php_laravel_job
  stage: security
  script:
    - mkdir -p reports
    - bash -o pipefail -c 'composer audit --locked --no-interaction 2>&1 | tee reports/composer-audit.log'
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - reports/
      - storage/logs/

npm_audit:
  image: node:22-alpine
  stage: security
  before_script:
    - npm ci
  script:
    - mkdir -p reports
    - sh -c 'npm audit --audit-level=high 2>&1 | tee reports/npm-audit.log'
  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 obvious committed secrets..." | tee reports/secret-detection-basic.log
      ! grep -RInE "(APP_KEY=base64:|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 \
        . 2>&1 | tee -a reports/secret-detection-basic.log
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - reports/
  allow_failure: false

# Optional GitLab-native security scanners.
# These run only if your GitLab tier/project supports the templates.
include:
  - template: Jobs/SAST.gitlab-ci.yml
  - template: Jobs/Secret-Detection.gitlab-ci.yml
  - template: Jobs/Dependency-Scanning.gitlab-ci.yml