name: 'Preview Environment Keep Alive' on: repository_dispatch: types: [preview-environment] jobs: preview-environment: timeout-minutes: 310 runs-on: ubuntu-latest steps: - name: Checkout PR uses: actions/checkout@v4 with: ref: ${{ github.event.client_payload.pr_head_sha }} - name: Run compose setup run: | echo "Patching docker-compose.yml..." # change image to localbuild using yq yq eval 'del(.services.server.image)' -i packages/twenty-docker/docker-compose.yml yq eval '.services.server.build.context = "../../"' -i packages/twenty-docker/docker-compose.yml yq eval '.services.server.build.dockerfile = "./packages/twenty-docker/twenty/Dockerfile"' -i packages/twenty-docker/docker-compose.yml yq eval 'del(.services.worker.image)' -i packages/twenty-docker/docker-compose.yml yq eval '.services.worker.build.context = "../../"' -i packages/twenty-docker/docker-compose.yml yq eval '.services.worker.build.dockerfile = "./packages/twenty-docker/twenty/Dockerfile"' -i packages/twenty-docker/docker-compose.yml echo "Setting up .env file..." cp packages/twenty-docker/.env.example packages/twenty-docker/.env echo "Generating secrets..." echo "# === Randomly generated secrets ===" >> packages/twenty-docker/.env echo "APP_SECRET=$(openssl rand -base64 32)" >> packages/twenty-docker/.env echo "PG_DATABASE_PASSWORD=$(openssl rand -hex 16)" >> packages/twenty-docker/.env # Remove line below when true becomes the default value (soon) echo "CONFIG_VARIABLES_IN_DB_ENABLED=true" >> packages/twenty-docker/.env echo "Docker compose build..." cd packages/twenty-docker/ docker compose build working-directory: ./ - name: Create Tunnel id: expose-tunnel uses: codetalkio/expose-tunnel@v1.5.0 with: service: bore.pub port: 3000 - name: Start services with correct SERVER_URL run: | cd packages/twenty-docker/ # Update the SERVER_URL with the tunnel URL echo "Setting SERVER_URL to ${{ steps.expose-tunnel.outputs.tunnel-url }}" sed -i '/SERVER_URL=/d' .env echo "SERVER_URL=${{ steps.expose-tunnel.outputs.tunnel-url }}" >> .env # Start the services echo "Docker compose up..." docker compose up -d || { echo "Docker compose failed to start" docker compose logs exit 1 } echo "Waiting for services to be ready..." count=0 while [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-db-1) = "healthy" ] || [ ! $(docker inspect --format='{{.State.Health.Status}}' twenty-server-1) = "healthy" ]; do sleep 5 count=$((count+1)) if [ $count -gt 60 ]; then echo "Timeout waiting for services to be ready" docker compose logs exit 1 fi echo "Still waiting for services... ($count/60)" done echo "All services are up and running!" working-directory: ./ - name: Output tunnel URL to logs run: | echo "✅ Preview Environment Ready!" echo "🔗 Preview URL: ${{ steps.expose-tunnel.outputs.tunnel-url }}" echo "⏱️ This environment will be available for 5 hours" - name: Post comment on PR uses: actions/github-script@v6 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | const COMMENT_MARKER = ''; const commentBody = `${COMMENT_MARKER} 🚀 **Preview Environment Ready!** Your preview environment is available at: ${{ steps.expose-tunnel.outputs.tunnel-url }} This environment will automatically shut down when the PR is closed or after 5 hours.`; // Get all comments const {data: comments} = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ github.event.client_payload.pr_number }}, }); // Find our comment const botComment = comments.find(comment => comment.body.includes(COMMENT_MARKER)); if (botComment) { // Update existing comment await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: botComment.id, body: commentBody }); console.log('Updated existing comment'); } else { // Create new comment await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ github.event.client_payload.pr_number }}, body: commentBody }); console.log('Created new comment'); } - name: Keep tunnel alive for 5 hours run: timeout 300m sleep 18000 # Stop on whichever we reach first (300m or 5hour sleep) - name: Cleanup if: always() run: | cd packages/twenty-docker/ docker compose down -v working-directory: ./