From 0d5f9eff7e1a67dd3848ad9ef01bd81c311a46ef Mon Sep 17 00:00:00 2001 From: kirby Date: Wed, 28 May 2025 11:53:16 +0200 Subject: [PATCH] add gitlab section --- gitlab/cicd/common-workflows.yml | 32 ++++++ gitlab/cicd/docker-k8s-utils.yml | 108 +++++++++++++++++++ gitlab/cicd/go.yml | 141 ++++++++++++++++++++++++ gitlab/cicd/php-symfony.yml | 179 +++++++++++++++++++++++++++++++ gitlab/cicd/utils.yml | 29 +++++ 5 files changed, 489 insertions(+) create mode 100644 gitlab/cicd/common-workflows.yml create mode 100644 gitlab/cicd/docker-k8s-utils.yml create mode 100644 gitlab/cicd/go.yml create mode 100644 gitlab/cicd/php-symfony.yml create mode 100644 gitlab/cicd/utils.yml diff --git a/gitlab/cicd/common-workflows.yml b/gitlab/cicd/common-workflows.yml new file mode 100644 index 0000000..b0d9eba --- /dev/null +++ b/gitlab/cicd/common-workflows.yml @@ -0,0 +1,32 @@ +workflow: + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: never + # NO DEPLOYMENT ON CLASSICS BRANCH + - if: '$CI_COMMIT_REF_PROTECTED == "false"' + variables: + ENVIRONMENT: "null" + ENVIRONMENT_SHORT: "null" + IMAGE_TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG + # TESTING - develop only + - if: '$CI_COMMIT_TAG == null && $CI_COMMIT_REF_NAME == "develop" && $CI_COMMIT_REF_PROTECTED == "true"' + variables: + ENVIRONMENT: testing + ENVIRONMENT_SHORT: tst + IMAGE_TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG + # STAGING - main/master only + - if: '($CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_REF_NAME == "master") && $CI_COMMIT_REF_PROTECTED == "true"' + variables: + ENVIRONMENT: staging + ENVIRONMENT_SHORT: stg + IMAGE_TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG + # PRODUCTION - tags only + - if: '$CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED == "true"' + variables: + ENVIRONMENT: production + ENVIRONMENT_SHORT: prd + IMAGE_TAG: $CI_COMMIT_TAG + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG diff --git a/gitlab/cicd/docker-k8s-utils.yml b/gitlab/cicd/docker-k8s-utils.yml new file mode 100644 index 0000000..e452e6a --- /dev/null +++ b/gitlab/cicd/docker-k8s-utils.yml @@ -0,0 +1,108 @@ +stages: + - init + - build + - scan + - deploy + +# ########################################### +# Configuration section +# ########################################### +.helminit: &helminit + before_script: + - echo "Adding Helm repository..." + - helm repo add --username $CI_REGISTRY_USER --password $CI_JOB_TOKEN helm-charts $CI_API_V4_URL/projects/645/packages/helm/stable + - helm repo update + - echo "Validating Helm dependencies..." + - helm dependency update ./helm + +# ########################################### +# Build section +# ########################################### +.build_template: &build_template + image: + name: moby/buildkit:v0.21.0 + entrypoint: [""] + variables: + DOCKER_BUILDKIT: 1 + before_script: + - echo "Preparing BuildKit environment..." + - mkdir -p /root/.docker + - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /root/.docker/config.json + - echo "Initializing build arguments..." + - | + for VARNAME in $BUILD_ARGS; do + VALUE=$(eval echo \$$VARNAME) + BUILDPARAMS="$BUILDPARAMS --opt build-arg:$VARNAME=$VALUE" + done + echo "Build parameters: $BUILDPARAMS" + script: + - echo "Validating build context..." + - ls -la $CI_PROJECT_DIR + - echo "Starting BuildKit build with the following settings:" + - | + buildctl-daemonless.sh build --frontend dockerfile.v0 \ + --local context=. \ + --local dockerfile=. \ + --output type=image,name=${BUILD_IMAGE_DESTINATION},push=true \ + --export-cache type=registry,ref=${CI_REGISTRY_IMAGE}/cache,image-manifest=true \ + --import-cache type=registry,ref=${CI_REGISTRY_IMAGE}/cache \ + --opt target=${BUILD_TARGET} \ + ${BUILDPARAMS} + +.build:image: + <<: *build_template + variables: + BUILD_IMAGE_DESTINATION: $CONTAINER_IMAGE + BUILD_TARGET: + BUILD_ARGS: + +# ########################################### +# Deploy section +# ########################################### +.deploy:k8s: + image: alpine/helm:3.21.0 + variables: + HELM_CUSTOM_ARGS: "" + NAMESPACE: tests + HPA_REPLICAS: 1 + HPA_MAXREPLICAS: 1 + HELM_NAME: $CI_PROJECT_NAME + INJECT_ENVVARS_GITLAB: "false" + VALUES_ENVVARS_PATH: symfonyLib.phpfpm.envVars. + before_script: + - !reference [".helminit", before_script] + - | + if [[ "$INJECT_ENVVARS_GITLAB" == "true" ]]; then + echo "Injecting environment variables into Helm Chart..." + for VARNAME in $(env); do + if [[ $(echo $VARNAME | egrep '^ENV_') ]]; then + NAME=$(echo "$VARNAME" | cut -d"=" -f1 | sed "s/ENV_/$VALUES_ENVVARS_PATH/") + VAR=$(echo "$VARNAME" | cut -d"=" -f2-) + echo -e $NAME + ENVVARS="${ENVVARS} --set $NAME=$VAR" + fi + done + fi + script: + - echo "Validating Helm chart..." + - helm lint ./helm # Validation du chart Helm avant déploiement + - echo "Deploying to $ENVIRONMENT k8s cluster in $NAMESPACE namespace..." + - | + helm upgrade --install --namespace $NAMESPACE -f ./helm/values.yaml $HELM_CUSTOM_ARGS \ + --set global.app.env="$ENVIRONMENT_SHORT" \ + --set global.replica.replicaCount="$HPA_REPLICAS" \ + --set global.replica.maxReplicaCount="$HPA_MAXREPLICAS" \ + --set global.app.version="$IMAGE_TAG" \ + --set global.namespace="$NAMESPACE" \ + --set global.app.revision="$CI_COMMIT_SHORT_SHA" \ + --set symfonyLib.phpfpm.image="$CI_REGISTRY_IMAGE" \ + --set pythonLib.python.image="$CI_REGISTRY_IMAGE" \ + $ENVVARS $HELM_NAME ./helm + - if [ $? -eq 0 ]; then touch success; fi + after_script: + - | + if [ -f 'success' ] && [ "$ENVIRONMENT" == 'production' ]; then + echo 'Sending notification to Teams webhook...' + apk add curl + curl -H 'Content-Type: application/json' -d "{\"text\": \"[prd] [job/$CI_JOB_NAME] [$CI_PROJECT_NAME] [$CI_COMMIT_REF_NAME] [$CI_PIPELINE_URL] completed!\"}" $TEAMS_WEBHOOK + fi diff --git a/gitlab/cicd/go.yml b/gitlab/cicd/go.yml new file mode 100644 index 0000000..312a5b4 --- /dev/null +++ b/gitlab/cicd/go.yml @@ -0,0 +1,141 @@ +include: + - project: $TEMPLATES_PROJECT_NAME + file: 'docker-k8s-utils.yml' + ref: $TEMPLATES_DEFAULT_BRANCH + - project: $TEMPLATES_PROJECT_NAME + file: 'utils.yml' + ref: $TEMPLATES_DEFAULT_BRANCH + +workflow: + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: never + # NO DEPLOYMENT + - if: '$CI_COMMIT_TAG == null' + variables: + IMAGE_TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG + changes: + - app/* + - app/*/* + - Dockerfile + - .gitlab-ci.yml + # STAGING/PRODUCTION - tags only + - if: '$CI_COMMIT_TAG =~ /(alpha)|(beta)|(rc)/' + variables: + IMAGE_TAG: $CI_COMMIT_TAG + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG + ENVIRONMENT: 'staging' + ENVIRONMENT_SHORT: 'stg' + - if: '$CI_COMMIT_TAG' + variables: + IMAGE_TAG: $CI_COMMIT_TAG + CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$IMAGE_TAG + ENVIRONMENT: 'production' + ENVIRONMENT_SHORT: 'prd' + +variables: + SRC_ROOT_PATH: "./app" + VAULT_ADDR: https://vault.example.com + +stages: + - lint + - build + - test + - release + +variables:checker: + extends: .variables:checker + stage: .pre + variables: + VARS_TO_CHECK_APP: "" + VARS_TO_CHECK_SYSTEM: SRC_ROOT_PATH CONTAINER_IMAGE + +# ########################################### +# Lint +# ########################################### + +linter: + image: golangci/golangci-lint:v2.0.2-alpine + rules: + - changes: + - .gitlab-ci.yml + - app/* + - app/*/* + when: always + stage: lint + before_script: + - cd $SRC_ROOT_PATH + script: + - go mod tidy + - golangci-lint run --output.code-climate.path gl-code-quality-report.json --path-prefix /app/ + artifacts: + reports: + codequality: $SRC_ROOT_PATH/gl-code-quality-report.json +build:application_test: + extends: .build:image + stage: build + variables: + BUILD_TARGET: dev + BUILD_IMAGE_DESTINATION: ${CONTAINER_IMAGE}_dev + BUILD_ARGS: "" + +####################################### +# TEST +####################################### + +go:tests: + image: ${CONTAINER_IMAGE}_dev + stage: test + before_script: + - cd $SRC_ROOT_PATH + - go mod tidy + - go install github.com/jstemmer/go-junit-report@latest + - go install github.com/boumenot/gocover-cobertura@latest + script: + - echo "Execute tests" + - go test ./... -v | go-junit-report > report-gotest.xml + - echo "Compute coverage" + - go test ./... -v -coverprofile=coverage.txt -covermode count + - gocover-cobertura < coverage.txt > report-coverage.xml + coverage: '/^coverage: \d+.\d+% of statements/' + artifacts: + reports: + junit: $SRC_ROOT_PATH/report-gotest.xml + coverage_report: + coverage_format: cobertura + path: $SRC_ROOT_PATH/report-coverage.xml + +####################################### +# BUILD +# ##################################### + +build:image: + extends: .build:image + stage: build + rules: + - if: '$CI_COMMIT_TAG' + when: on_success + variables: + BUILD_TARGET: prod + BUILD_IMAGE_DESTINATION: $CONTAINER_IMAGE + BUILD_ARGS: "" + +# ########################################### +# Release +# ########################################### +release:image: + image: hashicorp/vault:1.19.3 + stage: release + environment: $ENVIRONMENT + before_script: + - echo "Authentification Vault" + - export VAULT_TOKEN=$(vault write -field=token auth/approle/login role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID) + script: + - echo "Push last tag on Vault" + - vault kv put app//$ENVIRONMENT_SHORT/image_latest name=$CONTAINER_IMAGE + needs: + - build:image + rules: + - if: '$CI_COMMIT_TAG' + when: on_success diff --git a/gitlab/cicd/php-symfony.yml b/gitlab/cicd/php-symfony.yml new file mode 100644 index 0000000..c28ab47 --- /dev/null +++ b/gitlab/cicd/php-symfony.yml @@ -0,0 +1,179 @@ +# ########################################### +# Security section +# ########################################### +.security:advisories: + image: /docker/local-php-security-checker:1.0.0 + allow_failure: true + script: + - cd $SRC_ROOT_PATH + - local-php-security-checker --format=junit src > ./report-security-checker.xml + artifacts: + reports: + junit: $SRC_ROOT_PATH/report-security-checker.xml + +# ########################################### +# Qualimetry section +# ########################################### +.php:lint: + image: jakzal/phpqa:php${PHP_VERSION}${JAKZAL_PHP_VERSION}-alpine + script: + - cd $SRC_ROOT_PATH + - parallel-lint . --exclude vendor + +.php:checkstyle: + image: jakzal/phpqa:php${PHP_VERSION}${JAKZAL_PHP_VERSION}-alpine + script: + - cd $SRC_ROOT_PATH + - phpcs --extensions=php -n --standard=PSR12 src tests --report=junit > ./report-phpcs.xml + artifacts: + reports: + junit: $SRC_ROOT_PATH/report-phpcs.xml + +.php:stan: + image: jakzal/phpqa:php${PHP_VERSION}${JAKZAL_PHP_VERSION}-alpine + variables: + STAN_LEVEL: 5 + script: + - echo ${STAN_LEVEL} + - cd $SRC_ROOT_PATH + - phpstan analyse src --level=$STAN_LEVEL --no-progress --error-format=junit > ./report-phpstan.xml + artifacts: + reports: + junit: $SRC_ROOT_PATH/report-phpstan.xml + +# ########################################### +# Tests section +# ########################################### +.test:unit: + image: $CONTAINER_IMAGE + variables: + APP_ENV: test + XDEBUG_MODE: coverage + before_script: + - install-php-extensions xdebug-stable + - cp ./docker/env/.env.test $SRC_ROOT_PATH/.env + - cd $SRC_ROOT_PATH + - composer install --prefer-dist --no-progress --no-interaction + - bin/console cache:warmup --env=test + script: + - vendor/bin/simple-phpunit --coverage-text --colors=never --log-junit ./report-phpunit.xml + coverage: '/^\s*Lines:\s*\d+.\d+\%/' + artifacts: + reports: + junit: $SRC_ROOT_PATH/report-phpunit.xml + +.test:phpunit: + image: ${CONTAINER_IMAGE}_dev + variables: + APP_ENV: test + XDEBUG_MODE: coverage + GIT_STRATEGY: none + SRC_ROOT_PATH: /app + before_script: + - cd $SRC_ROOT_PATH + # Composer setup + - composer install --prefer-dist --no-progress --no-interaction + - composer dump-autoload --classmap-authoritative + - composer dump-env test + - composer run-script post-install-cmd + script: + - cd $SRC_ROOT_PATH + - ls -la + - vendor/bin/simple-phpunit --coverage-text --colors=never --log-junit="${CI_PROJECT_DIR}"/report-phpunit.xml + coverage: '/^\s*Lines:\s*\d+.\d+\%/' + artifacts: + reports: + junit: ./report-phpunit.xml + +.test:phpunit:standalone: + image: ${CONTAINER_IMAGE}_dev + variables: + APP_ENV: test + XDEBUG_MODE: coverage + GIT_STRATEGY: none + SRC_ROOT_PATH: /app + script: + - cd $SRC_ROOT_PATH + - vendor/bin/phpunit --coverage-text --colors=never --log-junit="${CI_PROJECT_DIR}"/report-phpunit.xml + coverage: '/^\s*Lines:\s*\d+.\d+\%/' + artifacts: + reports: + junit: ./report-phpunit.xml + +.test:codeception: + image: ${CONTAINER_IMAGE}_dev + variables: + APP_ENV: test + XDEBUG_MODE: coverage + GIT_STRATEGY: none + SRC_ROOT_PATH: /app + before_script: + - cd $SRC_ROOT_PATH + - sed -i 's#http://web#http://127.0.0.1:80#g' $SRC_ROOT_PATH/tests/api.suite.yml + # Composer setup + - composer install --prefer-dist --no-progress --no-interaction + - composer dump-autoload --classmap-authoritative + - composer dump-env test + - composer run-script post-install-cmd + script: + - cd $SRC_ROOT_PATH && symfony local:server:start --port=80 --no-tls -d + - $SRC_ROOT_PATH/vendor/bin/codecept run --xml --coverage --coverage-text --no-colors && true; exit_code=$? + - symfony local:server:stop + - exit $exit_code + after_script: + # Copy directory to CI dir for artifacts + - cp -r $SRC_ROOT_PATH/tests $CI_PROJECT_DIR/ + coverage: '/^\s*Lines:\s*\d+.\d+\%/' + artifacts: + expire_in: 1 day + when: always + paths: + - ./tests/_build/output/ + reports: + junit: ./tests/_build/output/report.xml + +.test:codeception:noweb: + extends: .test:codeception + before_script: + - cd $SRC_ROOT_PATH + # Composer setup + - composer install --prefer-dist --no-progress --no-interaction + - composer dump-autoload --classmap-authoritative + - composer dump-env test + - composer run-script post-install-cmd + script: + - cd $SRC_ROOT_PATH + - $SRC_ROOT_PATH/vendor/bin/codecept run --xml --coverage --coverage-text --no-colors && true; exit_code=$? + - exit $exit_code + +.test:codeception5: + image: ${CONTAINER_IMAGE}_dev + variables: + APP_ENV: test + XDEBUG_MODE: coverage + GIT_STRATEGY: none + SRC_ROOT_PATH: /app + before_script: + - cd $SRC_ROOT_PATH + - sed -i 's#http://web#http://127.0.0.1:80#g' $SRC_ROOT_PATH/tests/Api.suite.yml + # Composer setup + - composer install --prefer-dist --no-progress --no-interaction + - composer dump-autoload --classmap-authoritative + - composer dump-env test + - composer run-script post-install-cmd + script: + - cd $SRC_ROOT_PATH && symfony local:server:start --port=80 --no-tls -d + - $SRC_ROOT_PATH/vendor/bin/codecept run --xml --coverage --coverage-text --no-colors && true; exit_code=$? + - symfony local:server:stop + - exit $exit_code + after_script: + # Copy directory to CI dir for artifacts + - cp -r $SRC_ROOT_PATH/tests $CI_PROJECT_DIR/ + coverage: '/^\s*Lines:\s*\d+.\d+\%/' + artifacts: + expire_in: 1 day + when: always + paths: + - ./tests/_output/ + reports: + junit: ./tests/_output/report.xml diff --git a/gitlab/cicd/utils.yml b/gitlab/cicd/utils.yml new file mode 100644 index 0000000..ad17cad --- /dev/null +++ b/gitlab/cicd/utils.yml @@ -0,0 +1,29 @@ +# Check that variables exists +.variables:checker: + variables: + VARS_TO_CHECK_SYSTEM: SRC_ROOT_PATH CONTAINER_IMAGE ENVIRONMENT ENVIRONMENT_SHORT + VARS_TO_CHECK_APP: + VARS_TO_CHECK: $VARS_TO_CHECK_SYSTEM $VARS_TO_CHECK_APP + script: + - echo "Checking of the existence of some variables..." + - | + checkVariables () { + TXT_RED="\e[91m" && TXT_GREEN="\e[92m" && TXT_CLEAR="\e[0m" + inError=0 + + for var in "$@" + do + echo -ne "\$${var} : " + if [ -z "${!var}" ]; then + echo -e "\xE2\x9D\x8C ${TXT_RED}not defined${TXT_CLEAR}"; + inError=1; + else + echo -e "\xE2\x9C\x94 ${TXT_GREEN}defined${TXT_CLEAR}"; + fi + done + + return $inError + } + + checkVariables $VARS_TO_CHECK + exit $?