preview-environment #680
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
echo "SIGN_IN_PREFILLED=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: Seed Dev Workspace | |
run: | | |
cd packages/twenty-docker/ | |
echo "Seeding full dev workspace..." | |
if ! docker compose exec -T server yarn command:prod -- workspace:seed:dev; then | |
echo "❌ Seeding full dev workspace failed. Dumping server logs..." | |
docker compose logs server | |
exit 1 | |
fi | |
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 = '<!-- PR_PREVIEW_ENV -->'; | |
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: ./ |