stages:
  - lint
  - test
  - build
  - security
  - docker
  - deploy

variables:
  NODE_VERSION: '20'
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: '/certs'
  NPM_CONFIG_CACHE: '$CI_PROJECT_DIR/.npm'
  REGISTRY: '$CI_REGISTRY_IMAGE'

cache:
  key:
    files:
      - package-lock.json
  paths:
    - node_modules/
    - .npm/

.node_template: &node_template
  image: node:${NODE_VERSION}-alpine
  before_script:
    - node --version
    - npm --version
    - npm install --legacy-peer-deps

lint:eslint:
  <<: *node_template
  stage: lint
  script:
    - npm run lint || echo "No lint script found"
  allow_failure: false

lint:typecheck:
  <<: *node_template
  stage: lint
  script:
    - npx tsc --noEmit
  allow_failure: false

test:unit:
  <<: *node_template
  stage: test
  script:
    - npm test || echo "No test script found"
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    when: always
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    paths:
      - coverage/
    expire_in: 1 week

build:app:
  <<: *node_template
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week
  needs:
    - lint:eslint
    - lint:typecheck
    - test:unit

test:e2e:
  image: mcr.microsoft.com/playwright:v1.57.0-jammy
  stage: test
  script:
    - npm install --legacy-peer-deps
    - npx playwright install --with-deps chromium
    - npm run test:e2e
  artifacts:
    when: always
    paths:
      - playwright-report/
      - test-results/
    expire_in: 1 week
  needs:
    - build:app
  allow_failure: false

security:audit:
  <<: *node_template
  stage: security
  script:
    - npm audit --audit-level=moderate || true
  allow_failure: true

security:trivy:
  stage: security
  image:
    name: aquasec/trivy:latest
    entrypoint: ['']
  script:
    - trivy fs --exit-code 0 --no-progress --format json --output trivy-report.json .
    - trivy fs --exit-code 1 --severity CRITICAL --no-progress .
  artifacts:
    reports:
      container_scanning: trivy-report.json
  allow_failure: true

docker:build:
  stage: docker
  image: docker:24-dind
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
    - docker buildx create --name multiarch --driver docker-container --use
    - docker buildx inspect --bootstrap
  script:
    - |
      docker buildx build \
        --platform linux/amd64,linux/arm64 \
        --tag $REGISTRY:$CI_COMMIT_REF_SLUG \
        --tag $REGISTRY:$CI_COMMIT_SHORT_SHA \
        --push \
        .
    - |
      if [ "$CI_COMMIT_REF_NAME" = "main" ]; then
        docker buildx build \
          --platform linux/amd64,linux/arm64 \
          --tag $REGISTRY:latest \
          --push \
          .
      fi
  needs:
    - build:app
    - test:unit
    - security:audit
  only:
    - main
    - develop
    - tags

deploy:staging:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - echo "Deploying to staging environment..."
    - echo "Image: $REGISTRY:develop-$CI_COMMIT_SHORT_SHA"
    - |
      curl -X POST $STAGING_WEBHOOK_URL \
        -H "Content-Type: application/json" \
        -d "{\"image\":\"$REGISTRY:develop\",\"sha\":\"$CI_COMMIT_SHORT_SHA\"}"
  environment:
    name: staging
    url: https://staging.codeforge.example.com
  needs:
    - docker:build
  only:
    - develop

deploy:production:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - echo "Deploying to production environment..."
    - echo "Image: $REGISTRY:main-$CI_COMMIT_SHORT_SHA"
    - |
      curl -X POST $PRODUCTION_WEBHOOK_URL \
        -H "Content-Type: application/json" \
        -d "{\"image\":\"$REGISTRY:latest\",\"sha\":\"$CI_COMMIT_SHORT_SHA\"}"
  environment:
    name: production
    url: https://codeforge.example.com
  needs:
    - docker:build
    - test:e2e
  when: manual
  only:
    - main
