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