ToolJet/.github/workflows/render-preview-deploy-v2.yml
2026-01-06 17:29:22 +05:30

876 lines
No EOL
38 KiB
YAML

name: Render Preview Deploy V2 (DockerHub-based)
# Version: 2.2
# Changes from v2.1:
# - Single image tag per PR (ce-pr-{NUMBER} and ee-pr-{NUMBER})
# - Removed commit SHA from tags
# - DockerHub cache updates same tag on each push
on:
pull_request_target:
types: [labeled, unlabeled, closed, synchronize]
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
BRANCH_NAME: ${{ github.event.pull_request.head.ref }}
DOCKERHUB_REPO: tooljet/tooljet-render
RENDER_OWNER_ID: tea-caeo4bj19n072h3dddc0
permissions:
pull-requests: write
issues: write
jobs:
# ═══════════════════════════════════════════════════════════════════════════
# COMMUNITY EDITION (CE)
# ═══════════════════════════════════════════════════════════════════════════
build-and-deploy-ce:
if: |
github.event.pull_request.head.repo.fork == false &&
github.event.action == 'labeled' &&
github.event.label.name == 'create-ce-review-app-v2'
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ env.BRANCH_NAME }}
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push CE Image
uses: docker/build-push-action@v4
with:
context: .
file: ./docker/ce-preview.Dockerfile
push: true
tags: ${{ env.DOCKERHUB_REPO }}:ce-pr-${{ env.PR_NUMBER }}
platforms: linux/amd64
cache-from: type=gha,scope=ce-pr-${{ env.PR_NUMBER }}
cache-to: type=gha,mode=max,scope=ce-pr-${{ env.PR_NUMBER }}
- name: Create Render Service
id: create-service
run: |
RESPONSE=$(curl --silent --request POST \
--url https://api.render.com/v1/services \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' \
--data '{
"autoDeploy": "no",
"name": "ToolJet CE V2 PR #${{ env.PR_NUMBER }}",
"ownerId": "${{ env.RENDER_OWNER_ID }}",
"slug": "tooljet-ce-v2-pr-${{ env.PR_NUMBER }}",
"suspended": "not_suspended",
"type": "web_service",
"envVars": [
{"key": "PG_HOST", "value": "localhost"},
{"key": "PG_PORT", "value": "5432"},
{"key": "PG_USER", "value": "postgres"},
{"key": "PG_PASS", "value": "postgres"},
{"key": "PG_DB", "value": "${{ env.PR_NUMBER }}-ce-v2"},
{"key": "TOOLJET_DB", "value": "${{ env.PR_NUMBER }}-ce-v2-tjdb"},
{"key": "TOOLJET_DB_HOST", "value": "localhost"},
{"key": "TOOLJET_DB_USER", "value": "postgres"},
{"key": "TOOLJET_DB_PASS", "value": "postgres"},
{"key": "TOOLJET_DB_PORT", "value": "5432"},
{"key": "PGRST_DB_PRE_CONFIG", "value": "postgrest.pre_config"},
{"key": "PGRST_DB_URI", "value": "postgres://postgres:postgres@localhost/${{ env.PR_NUMBER }}-ce-v2-tjdb"},
{"key": "PGRST_HOST", "value": "127.0.0.1:3000"},
{"key": "PGRST_JWT_SECRET", "value": "r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj"},
{"key": "PGRST_LOG_LEVEL", "value": "info"},
{"key": "PORT", "value": "80"},
{"key": "TOOLJET_HOST", "value": "https://tooljet-ce-v2-pr-${{ env.PR_NUMBER }}.onrender.com"},
{"key": "DISABLE_TOOLJET_TELEMETRY", "value": "true"},
{"key": "SMTP_ADDRESS", "value": "smtp.mailtrap.io"},
{"key": "SMTP_DOMAIN", "value": "smtp.mailtrap.io"},
{"key": "SMTP_PORT", "value": "2525"},
{"key": "SMTP_USERNAME", "value": "${{ secrets.RENDER_SMTP_USERNAME }}"},
{"key": "SMTP_PASSWORD", "value": "${{ secrets.RENDER_SMTP_PASSWORD }}"},
{"key": "TOOLJET_MARKETPLACE_URL", "value": "${{ secrets.MARKETPLACE_BUCKET }}"}
],
"serviceDetails": {
"disk": {
"name": "tooljet-ce-v2-pr-${{ env.PR_NUMBER }}-postgresql",
"mountPath": "/var/lib/postgresql/13/main",
"sizeGB": 10
},
"env": "image",
"envSpecificDetails": {
"imagePath": "docker.io/${{ env.DOCKERHUB_REPO }}:ce-pr-${{ env.PR_NUMBER }}"
},
"healthCheckPath": "/api/health",
"numInstances": 1,
"openPorts": [{"port": 80, "protocol": "TCP"}],
"plan": "standard",
"pullRequestPreviewsEnabled": "no",
"region": "oregon"
}
}')
echo "Response: $RESPONSE"
SERVICE_ID=$(echo $RESPONSE | jq -r '.service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Failed to create service"
echo "$RESPONSE" | jq .
exit 1
fi
echo "SERVICE_ID=$SERVICE_ID" >> $GITHUB_ENV
echo "✅ Service created: $SERVICE_ID"
- name: Wait for Deployment
run: |
echo "⏳ Waiting for deployment to start..."
sleep 30
for i in {1..60}; do
DEPLOY_STATUS=$(curl --silent --request GET \
--url "https://api.render.com/v1/services/${{ env.SERVICE_ID }}/deploys?limit=1" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].deploy.status // "pending"')
echo "Deploy status: $DEPLOY_STATUS (attempt $i/60)"
if [ "$DEPLOY_STATUS" == "live" ]; then
echo "✅ Deployment successful!"
break
elif [ "$DEPLOY_STATUS" == "failed" ] || [ "$DEPLOY_STATUS" == "canceled" ]; then
echo "❌ Deployment failed with status: $DEPLOY_STATUS"
exit 1
fi
sleep 30
done
- name: Comment Deployment URL
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = process.env.PR_NUMBER;
const serviceId = process.env.SERVICE_ID;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🚀 CE Review App V2 Deployed!\n\n` +
`| Resource | Link |\n` +
`|----------|------|\n` +
`| **App URL** | https://tooljet-ce-v2-pr-${prNumber}.onrender.com |\n` +
`| **Render Dashboard** | https://dashboard.render.com/web/${serviceId} |\n` +
`| **Docker Image** | \`tooljet/tooljet-render:ce-pr-${prNumber}\` |\n\n` +
`_Deployed using DockerHub-based pipeline (V2)_`
});
- name: Update Labels
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Remove trigger label
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'create-ce-review-app-v2'
});
} catch (e) { console.log('Label not found:', e.message); }
// Add active label
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['active-ce-review-app-v2']
});
# ─────────────────────────────────────────────────────────────────────────────
# CE: Rebuild and Redeploy on new commits
# ─────────────────────────────────────────────────────────────────────────────
rebuild-ce:
if: |
github.event.pull_request.head.repo.fork == false &&
github.event.action == 'synchronize'
runs-on: ubuntu-latest
steps:
- name: Check if CE V2 is active
id: check-active
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const labels = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const hasLabel = labels.data.some(l => l.name === 'active-ce-review-app-v2');
core.setOutput('is_active', hasLabel);
return hasLabel;
- name: Checkout PR branch
if: steps.check-active.outputs.is_active == 'true'
uses: actions/checkout@v4
with:
ref: ${{ env.BRANCH_NAME }}
fetch-depth: 0
- name: Set up Docker Buildx
if: steps.check-active.outputs.is_active == 'true'
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: steps.check-active.outputs.is_active == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push CE Image
if: steps.check-active.outputs.is_active == 'true'
uses: docker/build-push-action@v4
with:
context: .
file: ./docker/ce-preview.Dockerfile
push: true
tags: ${{ env.DOCKERHUB_REPO }}:ce-pr-${{ env.PR_NUMBER }}
platforms: linux/amd64
cache-from: type=gha,scope=ce-pr-${{ env.PR_NUMBER }}
cache-to: type=gha,mode=max,scope=ce-pr-${{ env.PR_NUMBER }}
- name: Trigger Render Redeploy
if: steps.check-active.outputs.is_active == 'true'
run: |
# Get service ID
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20CE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Service not found"
exit 1
fi
echo "SERVICE_ID=$SERVICE_ID" >> $GITHUB_ENV
# Trigger deploy
DEPLOY_RESPONSE=$(curl --silent --request POST \
--url "https://api.render.com/v1/services/$SERVICE_ID/deploys" \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' \
--data '{"clearCache": "do_not_clear"}')
echo "Deploy triggered: $DEPLOY_RESPONSE"
- name: Comment Rebuild Status
if: steps.check-active.outputs.is_active == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const sha = '${{ github.event.pull_request.head.sha }}'.substring(0, 7);
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🔄 CE Review App V2 Rebuilding\n\n` +
`New commit \`${sha}\` pushed. Image updated and deployment triggered.\n\n` +
`_Using cached layers for faster build_`
});
# ─────────────────────────────────────────────────────────────────────────────
# CE: Suspend
# ─────────────────────────────────────────────────────────────────────────────
suspend-ce:
if: |
github.event.action == 'labeled' &&
github.event.label.name == 'suspend-ce-review-app-v2'
runs-on: ubuntu-latest
steps:
- name: Suspend Render Service
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20CE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Service not found"
exit 1
fi
curl --silent --request POST \
--url "https://api.render.com/v1/services/$SERVICE_ID/suspend" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}'
echo "✅ Service suspended"
- name: Update Labels
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'active-ce-review-app-v2'
});
} catch (e) { console.log('Label not found:', e.message); }
# ─────────────────────────────────────────────────────────────────────────────
# CE: Resume
# ─────────────────────────────────────────────────────────────────────────────
resume-ce:
if: |
github.event.action == 'unlabeled' &&
github.event.label.name == 'suspend-ce-review-app-v2'
runs-on: ubuntu-latest
steps:
- name: Resume Render Service
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20CE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Service not found"
exit 1
fi
curl --silent --request POST \
--url "https://api.render.com/v1/services/$SERVICE_ID/resume" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}'
echo "✅ Service resumed (using existing image from DockerHub)"
- name: Update Labels
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['active-ce-review-app-v2']
});
# ─────────────────────────────────────────────────────────────────────────────
# CE: Destroy
# ─────────────────────────────────────────────────────────────────────────────
destroy-ce:
if: |
(github.event.action == 'labeled' && github.event.label.name == 'destroy-ce-review-app-v2') ||
github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Check if CE V2 exists
id: check-exists
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20CE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -n "$SERVICE_ID" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "SERVICE_ID=$SERVICE_ID" >> $GITHUB_ENV
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Delete Render Service
if: steps.check-exists.outputs.exists == 'true'
run: |
curl --silent --request DELETE \
--url "https://api.render.com/v1/services/${{ env.SERVICE_ID }}" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}'
echo "✅ Service deleted"
- name: Clean up Labels
if: steps.check-exists.outputs.exists == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const labelsToRemove = [
'destroy-ce-review-app-v2',
'suspend-ce-review-app-v2',
'active-ce-review-app-v2'
];
for (const label of labelsToRemove) {
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: label
});
} catch (e) { console.log(`Label ${label} not found`); }
}
# ═══════════════════════════════════════════════════════════════════════════
# ENTERPRISE EDITION (EE)
# ═══════════════════════════════════════════════════════════════════════════
build-and-deploy-ee:
if: |
github.event.pull_request.head.repo.fork == false &&
github.event.action == 'labeled' &&
github.event.label.name == 'create-ee-review-app-v2'
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ env.BRANCH_NAME }}
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push EE Image
uses: docker/build-push-action@v4
with:
context: .
file: ./docker/pre-release/ee/ee-preview.Dockerfile
push: true
tags: ${{ env.DOCKERHUB_REPO }}:ee-pr-${{ env.PR_NUMBER }}
platforms: linux/amd64
cache-from: type=gha,scope=ee-pr-${{ env.PR_NUMBER }}
cache-to: type=gha,mode=max,scope=ee-pr-${{ env.PR_NUMBER }}
build-args: |
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
BRANCH_NAME=${{ env.BRANCH_NAME }}
- name: Create Render Service
id: create-service
run: |
RESPONSE=$(curl --silent --request POST \
--url https://api.render.com/v1/services \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' \
--data '{
"autoDeploy": "no",
"name": "ToolJet EE V2 PR #${{ env.PR_NUMBER }}",
"ownerId": "${{ env.RENDER_OWNER_ID }}",
"slug": "tooljet-ee-v2-pr-${{ env.PR_NUMBER }}",
"suspended": "not_suspended",
"type": "web_service",
"envVars": [
{"key": "PG_HOST", "value": "localhost"},
{"key": "PG_PORT", "value": "5432"},
{"key": "PG_USER", "value": "postgres"},
{"key": "PG_PASS", "value": "postgres"},
{"key": "PG_DB", "value": "${{ env.PR_NUMBER }}-ee-v2"},
{"key": "TOOLJET_DB", "value": "${{ env.PR_NUMBER }}-ee-v2-tjdb"},
{"key": "TOOLJET_DB_HOST", "value": "localhost"},
{"key": "TOOLJET_DB_USER", "value": "postgres"},
{"key": "TOOLJET_DB_PASS", "value": "postgres"},
{"key": "TOOLJET_DB_PORT", "value": "5432"},
{"key": "PGRST_DB_PRE_CONFIG", "value": "postgrest.pre_config"},
{"key": "PGRST_DB_URI", "value": "postgres://postgres:postgres@localhost/${{ env.PR_NUMBER }}-ee-v2-tjdb"},
{"key": "PGRST_HOST", "value": "127.0.0.1:3000"},
{"key": "PGRST_JWT_SECRET", "value": "r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj"},
{"key": "PGRST_LOG_LEVEL", "value": "info"},
{"key": "PORT", "value": "80"},
{"key": "TOOLJET_HOST", "value": "https://tooljet-ee-v2-pr-${{ env.PR_NUMBER }}.onrender.com"},
{"key": "DISABLE_TOOLJET_TELEMETRY", "value": "true"},
{"key": "SMTP_ADDRESS", "value": "smtp.mailtrap.io"},
{"key": "SMTP_DOMAIN", "value": "smtp.mailtrap.io"},
{"key": "SMTP_PORT", "value": "2525"},
{"key": "SMTP_USERNAME", "value": "${{ secrets.RENDER_SMTP_USERNAME }}"},
{"key": "SMTP_PASSWORD", "value": "${{ secrets.RENDER_SMTP_PASSWORD }}"},
{"key": "REDIS_HOST", "value": "localhost"},
{"key": "REDIS_PORT", "value": "6379"},
{"key": "REDIS_DB", "value": "0"},
{"key": "REDIS_TLS_ENABLED", "value": "false"},
{"key": "REDIS_PASSWORD", "value": ""},
{"key": "WORKER", "value": "true"},
{"key": "TOOLJET_MARKETPLACE_URL", "value": "${{ secrets.MARKETPLACE_BUCKET }}"},
{"key": "BRANCH_NAME", "value": "${{ env.BRANCH_NAME }}"},
{"key": "CUSTOM_GITHUB_TOKEN", "value": "${{ secrets.CUSTOM_GITHUB_TOKEN }}"}
],
"serviceDetails": {
"disk": {
"name": "tooljet-ee-v2-pr-${{ env.PR_NUMBER }}-postgresql",
"mountPath": "/var/lib/postgresql/13/main",
"sizeGB": 10
},
"env": "image",
"envSpecificDetails": {
"imagePath": "docker.io/${{ env.DOCKERHUB_REPO }}:ee-pr-${{ env.PR_NUMBER }}"
},
"healthCheckPath": "/api/health",
"numInstances": 1,
"openPorts": [{"port": 80, "protocol": "TCP"}],
"plan": "pro",
"pullRequestPreviewsEnabled": "no",
"region": "oregon"
}
}')
echo "Response: $RESPONSE"
SERVICE_ID=$(echo $RESPONSE | jq -r '.service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Failed to create service"
echo "$RESPONSE" | jq .
exit 1
fi
echo "SERVICE_ID=$SERVICE_ID" >> $GITHUB_ENV
echo "✅ Service created: $SERVICE_ID"
- name: Wait for Deployment
run: |
echo "⏳ Waiting for deployment to start..."
sleep 30
for i in {1..60}; do
DEPLOY_STATUS=$(curl --silent --request GET \
--url "https://api.render.com/v1/services/${{ env.SERVICE_ID }}/deploys?limit=1" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].deploy.status // "pending"')
echo "Deploy status: $DEPLOY_STATUS (attempt $i/60)"
if [ "$DEPLOY_STATUS" == "live" ]; then
echo "✅ Deployment successful!"
break
elif [ "$DEPLOY_STATUS" == "failed" ] || [ "$DEPLOY_STATUS" == "canceled" ]; then
echo "❌ Deployment failed with status: $DEPLOY_STATUS"
exit 1
fi
sleep 30
done
- name: Comment Deployment URL
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = process.env.PR_NUMBER;
const serviceId = process.env.SERVICE_ID;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🚀 EE Review App V2 Deployed!\n\n` +
`| Resource | Link |\n` +
`|----------|------|\n` +
`| **App URL** | https://tooljet-ee-v2-pr-${prNumber}.onrender.com |\n` +
`| **Render Dashboard** | https://dashboard.render.com/web/${serviceId} |\n` +
`| **Docker Image** | \`tooljet/tooljet-render:ee-pr-${prNumber}\` |\n\n` +
`_Deployed using DockerHub-based pipeline (V2)_`
});
- name: Update Labels
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'create-ee-review-app-v2'
});
} catch (e) { console.log('Label not found:', e.message); }
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['active-ee-review-app-v2']
});
# ─────────────────────────────────────────────────────────────────────────────
# EE: Rebuild and Redeploy on new commits
# ─────────────────────────────────────────────────────────────────────────────
rebuild-ee:
if: |
github.event.pull_request.head.repo.fork == false &&
github.event.action == 'synchronize'
runs-on: ubuntu-latest
steps:
- name: Check if EE V2 is active
id: check-active
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const labels = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const hasLabel = labels.data.some(l => l.name === 'active-ee-review-app-v2');
core.setOutput('is_active', hasLabel);
return hasLabel;
- name: Checkout PR branch
if: steps.check-active.outputs.is_active == 'true'
uses: actions/checkout@v4
with:
ref: ${{ env.BRANCH_NAME }}
fetch-depth: 0
- name: Set up Docker Buildx
if: steps.check-active.outputs.is_active == 'true'
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: steps.check-active.outputs.is_active == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push EE Image
if: steps.check-active.outputs.is_active == 'true'
uses: docker/build-push-action@v4
with:
context: .
file: ./docker/pre-release/ee/ee-preview.Dockerfile
push: true
tags: ${{ env.DOCKERHUB_REPO }}:ee-pr-${{ env.PR_NUMBER }}
platforms: linux/amd64
cache-from: type=gha,scope=ee-pr-${{ env.PR_NUMBER }}
cache-to: type=gha,mode=max,scope=ee-pr-${{ env.PR_NUMBER }}
build-args: |
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
BRANCH_NAME=${{ env.BRANCH_NAME }}
- name: Trigger Render Redeploy
if: steps.check-active.outputs.is_active == 'true'
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20EE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Service not found"
exit 1
fi
echo "SERVICE_ID=$SERVICE_ID" >> $GITHUB_ENV
DEPLOY_RESPONSE=$(curl --silent --request POST \
--url "https://api.render.com/v1/services/$SERVICE_ID/deploys" \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' \
--data '{"clearCache": "do_not_clear"}')
echo "Deploy triggered: $DEPLOY_RESPONSE"
- name: Comment Rebuild Status
if: steps.check-active.outputs.is_active == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const sha = '${{ github.event.pull_request.head.sha }}'.substring(0, 7);
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🔄 EE Review App V2 Rebuilding\n\n` +
`New commit \`${sha}\` pushed. Image updated and deployment triggered.\n\n` +
`_Using cached layers for faster build_`
});
# ─────────────────────────────────────────────────────────────────────────────
# EE: Suspend
# ─────────────────────────────────────────────────────────────────────────────
suspend-ee:
if: |
github.event.action == 'labeled' &&
github.event.label.name == 'suspend-ee-review-app-v2'
runs-on: ubuntu-latest
steps:
- name: Suspend Render Service
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20EE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Service not found"
exit 1
fi
curl --silent --request POST \
--url "https://api.render.com/v1/services/$SERVICE_ID/suspend" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}'
echo "✅ Service suspended"
- name: Update Labels
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: 'active-ee-review-app-v2'
});
} catch (e) { console.log('Label not found:', e.message); }
# ─────────────────────────────────────────────────────────────────────────────
# EE: Resume
# ─────────────────────────────────────────────────────────────────────────────
resume-ee:
if: |
github.event.action == 'unlabeled' &&
github.event.label.name == 'suspend-ee-review-app-v2'
runs-on: ubuntu-latest
steps:
- name: Resume Render Service
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20EE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -z "$SERVICE_ID" ]; then
echo "❌ Service not found"
exit 1
fi
curl --silent --request POST \
--url "https://api.render.com/v1/services/$SERVICE_ID/resume" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}'
echo "✅ Service resumed (using existing image from DockerHub)"
- name: Update Labels
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['active-ee-review-app-v2']
});
# ─────────────────────────────────────────────────────────────────────────────
# EE: Destroy
# ─────────────────────────────────────────────────────────────────────────────
destroy-ee:
if: |
(github.event.action == 'labeled' && github.event.label.name == 'destroy-ee-review-app-v2') ||
github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Check if EE V2 exists
id: check-exists
run: |
SERVICE_ID=$(curl --silent --request GET \
--url 'https://api.render.com/v1/services?name=ToolJet%20EE%20V2%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | jq -r '.[0].service.id // empty')
if [ -n "$SERVICE_ID" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "SERVICE_ID=$SERVICE_ID" >> $GITHUB_ENV
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Delete Render Service
if: steps.check-exists.outputs.exists == 'true'
run: |
curl --silent --request DELETE \
--url "https://api.render.com/v1/services/${{ env.SERVICE_ID }}" \
--header 'accept: application/json' \
--header 'Authorization: Bearer ${{ secrets.RENDER_API_KEY }}'
echo "✅ Service deleted"
- name: Clean up Labels
if: steps.check-exists.outputs.exists == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const labelsToRemove = [
'destroy-ee-review-app-v2',
'suspend-ee-review-app-v2',
'active-ee-review-app-v2'
];
for (const label of labelsToRemove) {
try {
await github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: label
});
} catch (e) { console.log(`Label ${label} not found`); }
}