.review_app_scripts: assign_port: # Assign an unprivileged port for the web server based on MR ID. - | APP_PORT=$(( $CI_MERGE_REQUEST_ID % 64000 + 1024 )) WS_PORT=$(( $APP_PORT + 1000 )) ASSETS_PORT=$(( $APP_PORT + 2000 )) echo "Assigned port number $APP_PORT." build_docker_compose_files: - !reference [.review_app_scripts, assign_port] - | echo "Creating docker compose files…" curl https://raw.githubusercontent.com/zammad/zammad-docker-compose/master/docker-compose.yml > docker-compose.yml cat - < docker-compose.review-app.yml --- version: '3.8' services: zammad-init: environment: - ZAMMAD_HTTP_TYPE=https - ZAMMAD_FQDN=${APP_FQDN} zammad-railsserver: ports: - "127.0.0.1:${APP_PORT}:3000" zammad-websocket: ports: - "127.0.0.1:${WS_PORT}:6042" zammad-backup: profiles: - do-not-start YML_FILE cat - <> .env IMAGE_REPO=${CI_REGISTRY}/${CI_PROJECT_PATH} VERSION=${APP_NAME} NGINX_EXPOSE_PORT="127.0.0.1:${ASSETS_PORT}" ENV_FILE # cat docker-compose* .env # Check if containers were previously created, so that this is an update deployment - | APP_DEPLOYED_BEFORE=$(${DOCKER_COMPOSE_COMMAND} ps --all --quiet) if [ "$APP_DEPLOYED_BEFORE" ] then echo "This review app was previously deployed." else echo "This review app was not deployed yet." fi build_and_push_docker_image: # Build/update docker image and publish it to the GitLab container registry. - | echo "Build docker image…" docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY echo -e "\\e[0Ksection_start:`date +%s`:docker_build[collapsed=true]\\r\\e[0Kdocker build" if [ ! "${SKIP_DOCKER_BUILD}" ] then docker build --pull --no-cache -t $IMAGE_URL . docker push $IMAGE_URL fi echo -e "\\e[0Ksection_end:`date +%s`:docker_build\\r\\e[0K" create_auto_wizard_json: - | cat - < tmp/auto_wizard.json { "Token": "${CI_JOB_TOKEN}", "Users": [ { "login": "admin@example.com", "firstname": "Test Admin", "lastname": "Agent", "email": "admin@example.com", "password": "${CI_MERGE_REQUEST_ID}" }, { "login": "agent1@example.com", "firstname": "Agent 1", "lastname": "Test", "email": "agent1@example.com", "password": "${CI_MERGE_REQUEST_ID}", "roles": ["Agent"] } ], "Groups": [ { "name": "some group1", "users": ["admin@example.com", "agent1@example.com"] }, { "name": "Users", "users": ["admin@example.com", "agent1@example.com"], "signature": "default", "email_address_id": 1 } ], "Channels": [ { "id": 1, "area": "Email::Account", "group": "Users", "options": { "inbound": { "adapter": "imap", "options": { "host": "mx1.example.com", "user": "not_existing", "password": "not_existing", "ssl": true } }, "outbound": { "adapter": "sendmail" } } } ], "EmailAddresses": [ { "id": 1, "channel_id": 1, "name": "Zammad Helpdesk", "email": "zammad@localhost" } ], "Settings": [ { "name": "product_name", "value": "Zammad Review App ${APP_NAME}" }, { "name": "http_type", "value": "https" }, { "name": "fqdn", "value": "${APP_FQDN}" }, { "name": "developer_mode", "value": true } ], "TextModuleLocale": { "Locale": "de-de" } } JSON_FILE create_vhost_config: - | cat - < /etc/nginx/conf.d/vhost-includes/${APP_NAME}.conf server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${APP_SLUG}.${SERVER_NAME}; client_max_body_size 50M; location ~ ^/(assets/|robots.txt|humans.txt|favicon.ico|apple-touch-icon.png) { expires max; proxy_pass http://127.0.0.1:${ASSETS_PORT}; } location / { proxy_set_header Host \$http_host; proxy_set_header CLIENT_IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; # Change this line in an SSO setup proxy_set_header X-Forwarded-User ""; proxy_read_timeout 300; proxy_pass http://127.0.0.1:${APP_PORT}; gzip on; gzip_types text/plain text/xml text/css image/svg+xml application/javascript application/x-javascript application/json application/xml; gzip_proxied any; } # legacy web socket server location /ws { proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header CLIENT_IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_read_timeout 86400; proxy_pass http://127.0.0.1:${WS_PORT}; } # action cable location /cable { proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header CLIENT_IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_read_timeout 86400; proxy_pass http://127.0.0.1:${APP_PORT}; } } VHOST_CONFIG delete_vhost_config: - rm -f /etc/nginx/conf.d/vhost-includes/${APP_NAME}.conf reload_nginx: - sudo systemctl reload nginx.service .review-app: tags: - review-apps stage: pre cache: [] variables: SERVER_NAME: review-apps.dc.zammad.com APP_SLUG: $CI_COMMIT_REF_SLUG APP_NAME: review-app-${APP_SLUG} APP_FQDN: ${APP_SLUG}.${SERVER_NAME} IMAGE_URL: ${CI_REGISTRY}/${CI_PROJECT_PATH}:${APP_NAME} DOCKER_COMPOSE_COMMAND: docker compose -f docker-compose.yml -f docker-compose.review-app.yml -p ${APP_NAME} environment: name: ${APP_NAME} url: https://${APP_FQDN}#getting_started/auto_wizard/${CI_JOB_TOKEN} rules: - if: $CI_MERGE_REQUEST_ID when: manual allow_failure: true - when: never before_script: - !reference [.review_app_scripts, build_docker_compose_files] after_script: - echo "Clean-up project build directory…" # shell runners don't clean-up, see https://gitlab.com/gitlab-org/gitlab/-/issues/243716 - rm -rf $CI_PROJECT_DIR - echo "Perform docker clean-up…" - docker image prune --all --force - docker volume prune --all --force - docker network prune --force review-app:deploy: extends: - .review-app script: - !reference [.review_app_scripts, build_and_push_docker_image] # Spawn a new or update an existing docker compose application on the GitLab runner. # If starting did not complete, bring down the container again to free up resources. - | echo "Deploy review app…" ${DOCKER_COMPOSE_COMMAND} up --force-recreate --detach || FAILED=true if [ $FAILED ] then ${DOCKER_COMPOSE_COMMAND} down echo "Failed to bring up container, exiting." exit 1 fi # Always provide auto_wizard.json, because we don't know when it will be activated. - !reference [.review_app_scripts, create_auto_wizard_json] - docker cp tmp/auto_wizard.json ${APP_NAME}-zammad-railsserver-1:/opt/zammad - !reference [.review_app_scripts, create_vhost_config] - !reference [.review_app_scripts, reload_nginx] environment: on_stop: review-app:stop auto_stop_in: 1 month review-app:stop: extends: - .review-app variables: GIT_STRATEGY: none script: - echo "Stop review app…" - !reference [.review_app_scripts, delete_vhost_config] - !reference [.review_app_scripts, reload_nginx] - ${DOCKER_COMPOSE_COMMAND} down environment: action: stop review-app:restart: extends: - .review-app variables: GIT_STRATEGY: none script: - echo "Restart review app…" - ${DOCKER_COMPOSE_COMMAND} restart