name: CI Docker permissions: contents: read on: pull_request: merge_group: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: changed-files-check: if: github.event_name != 'merge_group' uses: ./.github/workflows/changed-files.yaml with: files: | packages/twenty-docker/** docker-compose.yml test-compose: needs: changed-files-check if: needs.changed-files-check.outputs.any_changed == 'true' timeout-minutes: 30 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Run compose run: | echo "Patching docker-compose.yml..." yq eval 'del(.services.server.image)' -i docker-compose.yml yq eval '.services.server.build.context = "../../"' -i docker-compose.yml yq eval '.services.server.build.dockerfile = "./packages/twenty-docker/twenty/Dockerfile"' -i docker-compose.yml yq eval '.services.server.build.target = "twenty"' -i docker-compose.yml yq eval '.services.server.restart = "no"' -i docker-compose.yml echo "Setting up .env file..." cp .env.example .env echo "Generating secrets..." echo "" >> .env echo "# === Randomly generated secrets ===" >>.env echo "APP_SECRET=$(openssl rand -base64 32)" >>.env echo "PGPASSWORD_SUPERUSER=$(openssl rand -hex 16)" >>.env echo "Docker compose up..." docker compose up -d || { echo "Docker compose failed to start" docker compose logs exit 1 } docker compose logs db server -f & pid=$! echo "Waiting for database to start..." count=0 while [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-db-1) = "healthy" ]; do sleep 1; count=$((count+1)); if [ $(docker inspect --format='{{.State.Status}}' twenty-db-1) = "exited" ]; then echo "Database exited" docker compose logs db exit 1 fi if [ $count -gt 300 ]; then echo "Failed to start database after 5 minutes" docker compose logs db exit 1 fi echo "Still waiting for database... (${count}/60)" done echo "Waiting for server to start..." count=0 while [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-server-1) = "healthy" ]; do sleep 1; count=$((count+1)); if [ $(docker inspect --format='{{.State.Status}}' twenty-server-1) = "exited" ]; then echo "Server exited" docker compose logs server exit 1 fi if [ $count -gt 300 ]; then echo "Failed to start server after 5 minutes" docker compose logs server exit 1 fi echo "Still waiting for server... (${count}/300s)" done working-directory: ./packages/twenty-docker/ test-app-dev: needs: changed-files-check if: needs.changed-files-check.outputs.any_changed == 'true' timeout-minutes: 30 runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Create frontend placeholder run: | mkdir -p packages/twenty-front/build echo 'CI placeholder' > packages/twenty-front/build/index.html - name: Build app-dev image run: | docker build \ --target twenty-app-dev \ -f packages/twenty-docker/twenty/Dockerfile \ -t twenty-app-dev-ci \ . - name: Start container run: | docker run -d --name twenty-app-dev \ -p 2020:2020 \ twenty-app-dev-ci docker logs twenty-app-dev -f & - name: Wait for server health run: | echo "Waiting for twenty-app-dev to become healthy..." count=0 while true; do status=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:2020/healthz 2>/dev/null || echo "000") if [ "$status" = "200" ]; then echo "Server is healthy!" curl -s http://localhost:2020/healthz break fi container_status=$(docker inspect --format='{{.State.Status}}' twenty-app-dev 2>/dev/null || echo "unknown") if [ "$container_status" = "exited" ]; then echo "Container exited unexpectedly" docker logs twenty-app-dev exit 1 fi count=$((count+1)) if [ $count -gt 300 ]; then echo "Server did not become healthy within 5 minutes" docker logs twenty-app-dev exit 1 fi echo "Still waiting... (${count}/300s) [HTTP ${status}]" sleep 1 done ci-test-docker-status-check: if: always() && !cancelled() timeout-minutes: 5 runs-on: ubuntu-latest needs: [changed-files-check, test-compose, test-app-dev] steps: - name: Fail job if any needs failed if: contains(needs.*.result, 'failure') run: exit 1