stages: - test - build - deploy test: stage: test image: laravelsail/php84-composer variables: APP_ENV: testing APP_KEY: base64:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= DB_CONNECTION: sqlite DB_DATABASE: ":memory:" before_script: - apt-get update && apt-get install -y libicu-dev - docker-php-ext-configure intl && docker-php-ext-install intl - composer install --no-interaction --prefer-dist script: - php artisan test rules: - when: always docker-build-push: stage: build image: docker:24 services: - docker:24-dind variables: DOCKER_TLS_CERTDIR: "/certs" IMAGE_TAG: "$CI_REGISTRY_IMAGE:${CI_COMMIT_SHORT_SHA}" IMAGE_LATEST: "$CI_REGISTRY_IMAGE:latest" before_script: - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" script: - docker build -t "$IMAGE_TAG" -t "$IMAGE_LATEST" . - docker push "$IMAGE_TAG" - docker push "$IMAGE_LATEST" rules: - if: '$CI_COMMIT_BRANCH == "main"' deploy-to-portainer: stage: deploy image: alpine:latest needs: - docker-build-push before_script: - apk add --no-cache curl jq gettext script: - envsubst < docker-compose.prod.yml > docker-compose.rendered.yml - | set -eu PORTAINER_BASE_URL="${PORTAINER_URL%/}" for required_var in PORTAINER_URL PORTAINER_API_KEY PORTAINER_ENDPOINT_ID PORTAINER_STACK_NAME; do eval "required_value=\${$required_var:-}" if [ -z "$required_value" ]; then echo "Missing required CI variable: ${required_var}" exit 1 fi done STACKS_RESPONSE=$(curl -sS -w "\n%{http_code}" \ -H "X-API-Key: $PORTAINER_API_KEY" \ "$PORTAINER_BASE_URL/api/stacks") STACKS_HTTP_CODE=$(echo "$STACKS_RESPONSE" | tail -n1) STACKS_JSON=$(echo "$STACKS_RESPONSE" | sed '$d') if [ "$STACKS_HTTP_CODE" -ge 400 ]; then echo "Failed to list stacks on Portainer (HTTP $STACKS_HTTP_CODE)" echo "$STACKS_JSON" exit 1 fi echo "$STACKS_JSON" | jq -e . >/dev/null STACK_ID=$(echo "$STACKS_JSON" | jq ".[] | select(.Name == \"$PORTAINER_STACK_NAME\") | .Id") STACK_FILE_PATH="docker-compose.rendered.yml" if [ -z "$STACK_ID" ]; then JSON_PAYLOAD=$(jq -n \ --arg name "$PORTAINER_STACK_NAME" \ --rawfile content "$STACK_FILE_PATH" \ '{name: $name, stackFileContent: $content, prune: true, fromAppTemplate: false}') CREATE_RESPONSE=$(curl -sS -w "\n%{http_code}" \ -X POST "$PORTAINER_BASE_URL/api/stacks/create/standalone/string?endpointId=$PORTAINER_ENDPOINT_ID" \ -H "X-API-Key: $PORTAINER_API_KEY" \ -H "Content-Type: application/json" \ --data "$JSON_PAYLOAD") CREATE_HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n1) CREATE_BODY=$(echo "$CREATE_RESPONSE" | sed '$d') if [ "$CREATE_HTTP_CODE" -ge 400 ]; then echo "Portainer stack creation failed (HTTP $CREATE_HTTP_CODE)" echo "$CREATE_BODY" exit 1 fi echo "Stack created successfully." else JSON_PAYLOAD=$(jq -n \ --arg id "$STACK_ID" \ --rawfile content "$STACK_FILE_PATH" \ '{id: ($id|tonumber), stackFileContent: $content, prune: false, pullImage: true}') UPDATE_RESPONSE=$(curl -sS -w "\n%{http_code}" \ -X PUT "$PORTAINER_BASE_URL/api/stacks/$STACK_ID?endpointId=$PORTAINER_ENDPOINT_ID&method=string" \ -H "X-API-Key: $PORTAINER_API_KEY" \ -H "Content-Type: application/json" \ --data "$JSON_PAYLOAD") UPDATE_HTTP_CODE=$(echo "$UPDATE_RESPONSE" | tail -n1) UPDATE_BODY=$(echo "$UPDATE_RESPONSE" | sed '$d') if [ "$UPDATE_HTTP_CODE" -ge 400 ]; then echo "Portainer stack update failed (HTTP $UPDATE_HTTP_CODE)" echo "$UPDATE_BODY" exit 1 fi echo "Stack updated successfully." fi rules: - if: '$CI_COMMIT_BRANCH == "main"'