image: php variables: DOCKER_DRIVER: overlay2 TEST_IMAGE: ${CI_REGISTRY_IMAGE}/engelsystem:${CI_COMMIT_REF_SLUG} TEST_IMAGE_NGINX: ${CI_REGISTRY_IMAGE}/nginx:${CI_COMMIT_REF_SLUG} RELEASE_IMAGE: ${CI_REGISTRY_IMAGE}/engelsystem:latest RELEASE_IMAGE_NGINX: ${CI_REGISTRY_IMAGE}/nginx:latest MYSQL_DATABASE: engelsystem MYSQL_USER: engel MYSQL_PASSWORD: engelsystem MYSQL_HOST: mariadb MYSQL_RANDOM_ROOT_PASSWORD: "yes" MYSQL_INITDB_SKIP_TZINFO: "yes" DOCROOT: /var/www/ stages: - validate - build - test - release - deploy - deploy-production - stop # # Validation # check-style: image: composer:latest stage: validate before_script: - composer --no-ansi global require squizlabs/php_codesniffer - export PATH=$PATH:$COMPOSER_HOME/vendor/bin script: - phpcs -p --no-colors --basepath="$PWD" check-editorconfig: image: mstruebing/editorconfig-checker stage: validate script: - ec -v validate-composer: image: composer:latest stage: validate script: - composer --no-ansi validate --strict validate-yarn: image: node:alpine stage: validate before_script: - yarn global add package-json-validator - export PATH=$PATH:~/.yarn/bin script: - pjv # # Build # .docker_template: &docker_definition image: docker:18 services: - docker:18-dind tags: - dind before_script: - docker login -u gitlab-ci-token -p "${CI_JOB_TOKEN}" "${CI_REGISTRY}" build-image-nginx: <<: *docker_definition stage: build needs: - check-editorconfig - validate-yarn artifacts: name: "${CI_JOB_NAME}_${CI_JOB_ID}_assets" expire_in: 1 day paths: - ./public/assets script: - docker build --pull -t "${TEST_IMAGE_NGINX}" -f docker/nginx/Dockerfile . - docker push "${TEST_IMAGE_NGINX}" - instance=$(docker create "${TEST_IMAGE_NGINX}") - docker cp "${instance}:/var/www/public/assets" public/ - docker rm "${instance}" build-image: <<: *docker_definition stage: build needs: - check-editorconfig - validate-composer - check-style script: - apk add -q git - VERSION="$(git describe --abbrev=0 --tags)-${CI_COMMIT_REF_NAME}+${CI_PIPELINE_ID}.${CI_COMMIT_SHORT_SHA}" - docker build --pull --build-arg VERSION="${VERSION}" -t "${TEST_IMAGE}" -f docker/Dockerfile . - docker push "${TEST_IMAGE}" # # Test # audit-composer: image: ${TEST_IMAGE} stage: test needs: [ build-image ] before_script: - wget -qO - https://github.com/symfony/cli/releases/latest/download/symfony_linux_amd64.gz | gzip -d > /bin/symfony - chmod +x /bin/symfony script: - symfony check:security --no-ansi --dir "${DOCROOT}" audit-yarn: image: node:alpine stage: test needs: [ ] script: - yarn audit test: image: ${TEST_IMAGE} stage: test needs: [ build-image ] services: - mariadb:10.2 artifacts: name: "${CI_JOB_NAME}_${CI_JOB_ID}" expire_in: 1 week when: always paths: - ./coverage/ - ./unittests.xml reports: junit: ./unittests.xml coverage: '/^\s*Lines:\s*(\d+(?:\.\d+)?%)/' before_script: - apk add -q ${PHPIZE_DEPS} && pecl install pcov > /dev/null && docker-php-ext-enable pcov - curl -sS https://getcomposer.org/installer | php -- --no-ansi --install-dir /usr/local/bin/ --filename composer - cp -R tests/ phpunit.xml "${DOCROOT}" - HOMEDIR=$PWD - cd "${DOCROOT}" - composer --no-ansi install - ./bin/migrate script: - >- php -d pcov.enabled=1 vendor/bin/phpunit -vvv --colors=never --coverage-text --coverage-html "${HOMEDIR}/coverage/" --log-junit "${HOMEDIR}/unittests.xml" after_script: - '"${DOCROOT}/bin/migrate" down' dump-database: image: ${TEST_IMAGE} stage: test needs: [ build-image ] services: - mariadb:10.2 artifacts: expire_in: 1 week paths: - initial-install.sql before_script: - apk add -q mariadb-client - HOMEDIR=$PWD - cd "${DOCROOT}" - ./bin/migrate script: - >- mysqldump -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASSWORD}" "${MYSQL_DATABASE}" > "${HOMEDIR}/initial-install.sql" # # Release # release-image: <<: *docker_definition stage: release needs: [ test ] dependencies: [ ] script: - docker pull "${TEST_IMAGE}" - docker tag "${TEST_IMAGE}" "${RELEASE_IMAGE}" - docker push "${RELEASE_IMAGE}" only: - main release-image-nginx: <<: *docker_definition stage: release needs: - test - build-image-nginx dependencies: [ ] script: - docker pull "${TEST_IMAGE_NGINX}" - docker tag "${TEST_IMAGE_NGINX}" "${RELEASE_IMAGE_NGINX}" - docker push "${RELEASE_IMAGE_NGINX}" only: - main .deploy_template: &deploy_definition stage: release image: ${TEST_IMAGE} before_script: - apk add -q bash rsync openssh-client build-release-file: <<: *deploy_definition stage: release needs: - build-image - build-image-nginx - audit-yarn - audit-composer - test - dump-database dependencies: - build-image - build-image-nginx - dump-database artifacts: name: release_${CI_COMMIT_REF_SLUG}_${CI_JOB_ID}_${CI_COMMIT_SHA} expire_in: 1 week paths: - ./release/ script: - rsync -vAax "${DOCROOT}" "${DOCROOT}/.babelrc" "${DOCROOT}/.browserslistrc" "initial-install.sql" release/ - rsync -vAax public/assets release/public/ pages: image: node:alpine stage: release needs: [ test ] dependencies: [ test ] script: - rm -rf public - mv coverage public - cp unittests.xml public/ artifacts: expire_in: 1 week paths: - public only: - main variables: GIT_STRATEGY: none # # Deploy staging # .deploy_template_script: # Configure SSH - &deploy_template_script |- eval $(ssh-agent -s) && echo "${SSH_PRIVATE_KEY}" | ssh-add - rsync -vAax public/assets ${DOCROOT}/public/ cd "${DOCROOT}" deploy: <<: *deploy_definition stage: deploy needs: &deploy_needs - release-image - build-image-nginx dependencies: *deploy_needs environment: name: staging only: - main script: # Check if deployment variables where set - |- if [ -z "${SSH_PRIVATE_KEY}" ] || [ -z "${STAGING_REMOTE}" ] || [ -z "${STAGING_REMOTE_PATH}" ]; then echo "Skipping deployment" exit fi - *deploy_template_script # Deploy to server - ./bin/deploy.sh -r "${STAGING_REMOTE}" -p "${STAGING_REMOTE_PATH}" -i "${CI_JOB_ID}-${CI_COMMIT_SHA}" .kubectl_deployment: &kubectl_deployment stage: deploy image: name: bitnami/kubectl:latest entrypoint: [ '' ] needs: - test - build-image - build-image-nginx before_script: - &kubectl_deployment_script if [[ -z "${KUBE_INGRESS_BASE_DOMAIN}" ]]; then echo "Skipping deployment"; exit; fi .deploy_k8s: &deploy_k8s <<: *kubectl_deployment dependencies: [ ] artifacts: name: deployment.yaml expire_in: 1 day when: always paths: - deployment.yaml script: # CI_ENVIRONMENT_URL is the URL configured in the GitLab environment - export CI_ENVIRONMENT_URL="${CI_ENVIRONMENT_URL:-https://${CI_PROJECT_PATH_SLUG}.${KUBE_INGRESS_BASE_DOMAIN}/}" - export CI_IMAGE=$RELEASE_IMAGE - export CI_IMAGE_NGINX=$RELEASE_IMAGE_NGINX - export CI_INGRESS_DOMAIN=$(echo "$CI_ENVIRONMENT_URL" | grep -oP '(?:https?://)?\K([^/]+)' | head -n1) - export CI_INGRESS_PATH=$(echo "$CI_ENVIRONMENT_URL" | grep -oP '(?:https?://)?(?:[^/])+\K(.*)') - export CI_KUBE_NAMESPACE=$KUBE_NAMESPACE # Any available storage class like longhorn - export CI_PVC_SC=${CI_PVC_SC:-"${CI_PVC_SC_LOCAL:-local-path}"} - export CI_REPLICAS=${CI_REPLICAS_REVIEW:-${CI_REPLICAS:-2}} - export CI_APP_NAME=${CI_APP_NAME:-Engelsystem} - export CI_CLUSTER_ISSUER=${CI_CLUSTER_ISSUER:-letsencrypt} - cp deployment.tpl.yaml deployment.yaml - for env in ${!CI_*}; do sed -i "s#<${env}>#$(echo "${!env}"|head -n1)#g" deployment.yaml; done - echo "Deploying to ${CI_ENVIRONMENT_URL}" - kubectl apply -f deployment.yaml - >- kubectl -n $CI_KUBE_NAMESPACE wait --for=condition=Ready pods --timeout=${CI_WAIT_TIMEOUT:-5}m -l app=$CI_PROJECT_PATH_SLUG -l tier=database - >- kubectl -n $CI_KUBE_NAMESPACE wait --for=condition=Ready pods --timeout=${CI_WAIT_TIMEOUT:-5}m -l app=$CI_PROJECT_PATH_SLUG -l tier=application -l commit=$CI_COMMIT_SHORT_SHA .deploy_k8s_stop: &deploy_k8s_stop <<: *kubectl_deployment stage: stop dependencies: [ ] variables: GIT_STRATEGY: none when: manual script: - kubectl delete all,ingress,pvc -l app=$CI_PROJECT_PATH_SLUG -l environment=$CI_ENVIRONMENT_SLUG deploy-k8s-review: <<: *deploy_k8s environment: name: review/${CI_COMMIT_REF_NAME} on_stop: stop-k8s-review auto_stop_in: 1 week url: https://${CI_PROJECT_PATH_SLUG}-review.${KUBE_INGRESS_BASE_DOMAIN}/${CI_COMMIT_REF_SLUG} variables: CI_REPLICAS_REVIEW: 1 CI_APP_NAME: review/${CI_COMMIT_REF_NAME} before_script: - *kubectl_deployment_script - RELEASE_IMAGE=$TEST_IMAGE - RELEASE_IMAGE_NGINX=$TEST_IMAGE_NGINX stop-k8s-review: <<: *deploy_k8s_stop needs: [ deploy-k8s-review ] environment: name: review/${CI_COMMIT_REF_NAME} action: stop # # Deploy production # deploy-production: <<: *deploy_definition stage: deploy-production needs: - test - audit-yarn - audit-composer - build-image - build-image-nginx dependencies: - build-image - build-image-nginx environment: name: production when: manual only: - main script: # Check if deployment variables where set - |- if [ -z "${SSH_PRIVATE_KEY}" ] || [ -z "${PRODUCTION_REMOTE}" ] || [ -z "${PRODUCTION_REMOTE_PATH}" ]; then echo "Skipping deployment" exit fi - *deploy_template_script # Deploy to server - ./bin/deploy.sh -r "${PRODUCTION_REMOTE}" -p "${PRODUCTION_REMOTE_PATH}" -i "${CI_JOB_ID}-${CI_COMMIT_SHA}" deploy-k8s-production: <<: *deploy_k8s stage: deploy-production needs: - release-image - release-image-nginx - audit-yarn - audit-composer environment: name: production on_stop: stop-k8s-production when: manual only: - main stop-k8s-production: <<: *deploy_k8s_stop needs: [ deploy-k8s-production ] only: - main environment: name: production action: stop