services:
  postgres:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_DB: tuist
      POSTGRES_USER: tuist
      POSTGRES_PASSWORD: tuist
    ports:
      - "${POSTGRES_PORT:-5433}:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U tuist -d tuist"]
      interval: 5s
      timeout: 5s
      retries: 20

  pgweb:
    image: sosedoff/pgweb:0.16.2
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      PGWEB_DATABASE_URL: postgres://tuist:tuist@postgres:5432/tuist?sslmode=disable
    ports:
      - "${PGWEB_PORT:-8081}:8081"

  clickhouse-keeper:
    image: clickhouse/clickhouse-server:26.5-alpine
    restart: unless-stopped
    command: ["clickhouse-keeper", "--config-file=/etc/clickhouse-keeper/keeper_config.xml"]
    volumes:
      - ./clickhouse-keeper-config.xml:/etc/clickhouse-keeper/keeper_config.xml:ro
      - clickhouse-keeper-data:/var/lib/clickhouse
    healthcheck:
      test: ["CMD-SHELL", "clickhouse-keeper-client --host 127.0.0.1 --port 9181 --query ruok | grep -q imok"]
      interval: 5s
      timeout: 5s
      retries: 20

  clickhouse:
    image: clickhouse/clickhouse-server:26.5-alpine
    restart: unless-stopped
    depends_on:
      clickhouse-keeper:
        condition: service_healthy
    environment:
      CLICKHOUSE_DB: tuist
      CLICKHOUSE_USER: tuist
      CLICKHOUSE_PASSWORD: tuist
      CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1"
      # Read only once the clickhouse-log-ttl.xml volume below is uncommented;
      # system log retention is off until then.
      CLICKHOUSE_SYSTEM_LOG_TTL: "event_date + INTERVAL ${CLICKHOUSE_SYSTEM_LOG_TTL_DAYS:-14} DAY DELETE"
      CLICKHOUSE_TEXT_LOG_LEVEL: ${CLICKHOUSE_TEXT_LOG_LEVEL:-information}
    ports:
      - "${CLICKHOUSE_HTTP_PORT:-8124}:8123"
      - "${CLICKHOUSE_NATIVE_PORT:-9004}:9000"
    volumes:
      - ./clickhouse-config.xml:/etc/clickhouse-server/config.d/tuist.xml:ro
      - ./clickhouse-log-level.xml:/etc/clickhouse-server/config.d/tuist-log-level.xml:ro
      # Retention for ClickHouse's internal system.* log tables, which are
      # unbounded otherwise. Uncomment to apply it, and set
      # CLICKHOUSE_SYSTEM_LOG_TTL_DAYS in .env to change the 14-day window.
      #
      # On an instance that already has data this is a one-time, one-way step.
      # Any TTL at all makes those table definitions stop matching, so ClickHouse
      # renames each one to `<name>_N` and starts a fresh table. The generation
      # left behind holds everything accumulated so far and carries no TTL of its
      # own, so set CLICKHOUSE_SUPERSEDED_LOG_TABLES=delete at the same time to
      # reclaim it. Without that it stays on disk for good and the window only
      # bounds rows written from then on.
      # - ./clickhouse-log-ttl.xml:/etc/clickhouse-server/config.d/tuist-log-ttl.xml:ro
      - clickhouse-data:/var/lib/clickhouse
    healthcheck:
      # BusyBox wget, which is what the alpine image ships, has no --user or
      # --password, so an HTTP probe can never authenticate here.
      test:
        [
          "CMD-SHELL",
          "clickhouse-client --user \"$$CLICKHOUSE_USER\" --password \"$$CLICKHOUSE_PASSWORD\" --database \"$$CLICKHOUSE_DB\" --query 'SELECT 1'",
        ]
      interval: 5s
      timeout: 5s
      retries: 30

  clickhouse-init:
    image: clickhouse/clickhouse-server:26.5-alpine
    depends_on:
      clickhouse:
        condition: service_healthy
    environment:
      CLICKHOUSE_USER: tuist
      CLICKHOUSE_PASSWORD: tuist
      # ClickHouse renames a system log table to `<name>_N` and starts a fresh
      # one whenever the table definition stops matching the config, and never
      # reclaims the old one. `ignore` keeps them, `delete` drops them.
      CLICKHOUSE_SUPERSEDED_LOG_TABLES: ${CLICKHOUSE_SUPERSEDED_LOG_TABLES:-ignore}
    entrypoint:
      - /bin/sh
      - -ec
      - |
        case "$$CLICKHOUSE_SUPERSEDED_LOG_TABLES" in
          ignore) exit 0 ;;
          delete) ;;
          *) echo "CLICKHOUSE_SUPERSEDED_LOG_TABLES must be ignore or delete, got '$$CLICKHOUSE_SUPERSEDED_LOG_TABLES'" >&2; exit 1 ;;
        esac
        ch() { clickhouse-client --host clickhouse --port 9000 --user "$$CLICKHOUSE_USER" --password "$$CLICKHOUSE_PASSWORD" "$$@"; }
        # Reclaiming disk must never keep the stack down, and the tables being
        # dropped are inert, so nothing below is allowed to fail this service.
        set +e
        # ClickHouse supersedes a system log table when it first flushes to it, not
        # when it starts serving, so the healthcheck passes several seconds
        # before this upgrade's generations exist. Forcing the flush makes them
        # all exist before we look.
        ch --query "SYSTEM FLUSH LOGS"
        # ClickHouse stops managing a table once it is superseded, so every
        # generation is inert and safe to drop, including ones for tables
        # clickhouse-log-ttl.xml cannot declare a TTL for.
        tables=$$(ch --query "SELECT name FROM system.tables WHERE database = 'system' AND engine = 'MergeTree' AND match(name, '_log_[0-9]+\$$')")
        for table in $$tables; do
          echo "dropping system.$$table"
          ch --query "DROP TABLE IF EXISTS system.\`$$table\` SYNC" || echo "could not drop system.$$table, leaving it in place" >&2
        done
        exit 0

  minio:
    image: minio/minio:RELEASE.2025-09-07T16-13-09Z
    restart: unless-stopped
    command: server /data --address ":9000" --console-address ":9003"
    environment:
      MINIO_ROOT_USER: tuist
      MINIO_ROOT_PASSWORD: tuist_dev_password
    ports:
      - "${MINIO_API_PORT:-9002}:9000"
      - "${MINIO_CONSOLE_PORT:-9003}:9003"
    volumes:
      - minio-data:/data
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:9000/minio/health/ready >/dev/null"]
      interval: 5s
      timeout: 5s
      retries: 20

  minio-init:
    image: minio/mc:RELEASE.2025-08-13T08-35-41Z
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set local http://minio:9000 tuist tuist_dev_password &&
      mc mb --ignore-existing local/tuist-artifacts &&
      mc mb --ignore-existing local/tuist-cache &&
      mc mb --ignore-existing local/tuist-xcode-cache
      "

  kura:
    image: ${KURA_IMAGE:-ghcr.io/tuist/kura:latest}
    restart: unless-stopped
    environment:
      # One listener serves the HTTP cache API and REAPI gRPC (h2c).
      KURA_PORT: "4000"
      KURA_INTERNAL_PORT: "7443"
      KURA_TENANT_ID: default
      KURA_REGION: local
      KURA_TMP_DIR: /tmp/kura
      KURA_DATA_DIR: /var/cache/kura
      KURA_NODE_URL: http://kura:7443
      KURA_PEERS: ""
      KURA_OTEL_SERVICE_NAME: kura-local
      KURA_OTEL_DEPLOYMENT_ENVIRONMENT: local
    ports:
      - "${KURA_HTTP_PORT:-4000}:4000"
    volumes:
      - kura-data:/var/cache/kura
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:4000/up >/dev/null"]
      interval: 5s
      timeout: 5s
      retries: 30

  tuist:
    image: ${TUIST_IMAGE:-ghcr.io/tuist/tuist:latest}
    restart: unless-stopped
    env_file:
      - .env
    depends_on:
      postgres:
        condition: service_healthy
      clickhouse:
        condition: service_healthy
      clickhouse-init:
        condition: service_completed_successfully
      minio-init:
        condition: service_completed_successfully
      kura:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://tuist:tuist@postgres:5432/tuist
      TUIST_USE_SSL_FOR_DATABASE: "0"
      TUIST_CLICKHOUSE_URL: http://tuist:tuist@clickhouse:8123/tuist
      TUIST_APP_URL: http://localhost:${TUIST_PORT:-8080}
      TUIST_HOSTED: "0"
      TUIST_S3_ACCESS_KEY_ID: tuist
      TUIST_S3_SECRET_ACCESS_KEY: tuist_dev_password
      TUIST_S3_BUCKET_NAME: tuist-artifacts
      TUIST_S3_REGION: auto
      TUIST_S3_ENDPOINT: http://minio:9000
      TUIST_CACHE_S3_BUCKET_NAME: tuist-cache
      TUIST_CACHE_XCODE_S3_BUCKET_NAME: tuist-xcode-cache
      TUIST_KURA_ENDPOINTS: http://kura:4000
      TUIST_SKIP_EMAIL_CONFIRMATION: "true"
      TUIST_LOG_LEVEL: info
    ports:
      - "${TUIST_PORT:-8080}:8080"
      - "${TUIST_METRICS_PORT:-9091}:9091"
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/ready >/dev/null"]
      interval: 10s
      timeout: 5s
      retries: 30

volumes:
  postgres-data:
  clickhouse-data:
  clickhouse-keeper-data:
  minio-data:
  kura-data:
