-
-
Save ursuleacv/4a0f979674038f6b02217c9086af096a to your computer and use it in GitHub Desktop.
Automatically deploy PRs to preview environments with the help of Laravel Forge
This file contains 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 | |
on: | |
pull_request: | |
types: [opened] | |
env: | |
FORGE_API_TOKEN: ${{ secrets.FORGE_API_TOKEN }} | |
FORGE_SERVER_ID: ${{ secrets.FORGE_SERVER_ID }} | |
jobs: | |
provision_site: | |
runs-on: ubuntu-latest | |
if: github.event.action == 'opened' | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v2 | |
- name: Define Variables | |
run: | | |
echo "PR_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV | |
echo "PR_NUMBER=${{ github.event.number }}" >> $GITHUB_ENV | |
echo "SITE_URL=https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites" >> $GITHUB_ENV | |
echo "DATABASE_URL=https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/databases" >> $GITHUB_ENV | |
- name: Create Site | |
id: create_site | |
run: | | |
SITE_CREATION_RESPONSE=$(curl -s -X POST "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json" \ | |
-d "{\"domain\": \"$PR_NUMBER.dev.example.com\", \"project_type\": \"php\", \"directory\": \"/public\", \"database\": \"$PR_NUMBER\"}") | |
echo "Site Creation Response: $SITE_CREATION_RESPONSE" | |
SITE_ID=$(echo "$SITE_CREATION_RESPONSE" | jq -r '.site.id') | |
echo "SITE_ID=$SITE_ID" >> $GITHUB_ENV | |
echo "::set-output name=SITE_ID::$SITE_ID" | |
- name: Check Site Status | |
run: | | |
SITE_STATUS="installing" | |
while [ "$SITE_STATUS" != "installed" ]; do | |
echo "Checking site status..." | |
SITE_STATUS_RESPONSE=$(curl -s -X GET "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json") | |
SITE_STATUS=$(echo "$SITE_STATUS_RESPONSE" | jq -r '.site.status') | |
if [ "$SITE_STATUS" != "installed" ]; then | |
echo "Site status: $SITE_STATUS. Waiting for installation to complete..." | |
sleep 30 | |
fi | |
done | |
echo "Site setup is complete." | |
- name: Install PR Branch to New Site | |
run: | | |
INSTALL_RESPONSE=$(curl -s -X POST $SITE_URL/$SITE_ID/git -H "Authorization: Bearer $FORGE_API_TOKEN" -H "Accept: application/json" -H "Content-Type: application/json" -d "{\"provider\":\"custom\",\"repository\":\"[email protected]:your-repo/your-app\",\"branch\":\"$PR_BRANCH\",\"composer\":true}") | |
if echo $INSTALL_RESPONSE | grep -q "error"; then | |
echo "Failed to install PR branch to new site" | |
exit 1 | |
fi | |
- name: Check Git Repository Status | |
run: | | |
API_RESPONSE=$(curl -s -X GET "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json") | |
echo "API Response: $API_RESPONSE" | |
if [[ -z "$API_RESPONSE" ]] || ! (echo "$API_RESPONSE" | jq . > /dev/null 2>&1); then | |
echo "Error: Invalid or empty response from Forge API" | |
exit 1 | |
fi | |
SITE_STATUS=$(echo "$API_RESPONSE" | jq -r '.site.repository_status') | |
while [ "$SITE_STATUS" != "installed" ]; do | |
echo "Waiting for site to be ready. Current status: $SITE_STATUS" | |
sleep 15 | |
API_RESPONSE=$(curl -s -X GET "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json") | |
SITE_STATUS=$(echo "$API_RESPONSE" | jq -r '.site.repository_status') | |
done | |
echo "Git install is ready." | |
- name: Update Deployment Script | |
run: | | |
DEPLOY_SCRIPT_UPDATE_RESPONSE=$(curl -s -X PUT "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/deployment/script" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json" \ | |
-d "{ | |
\"content\": \"cd /home/forge/$PR_NUMBER.dev.example.com\\ngit pull origin \$FORGE_SITE_BRANCH\\n\\ncomposer install --no-interaction --prefer-dist --optimize-autoloader\\n\\n( flock -w 10 9 || exit 1\\n echo 'Restarting FPM...'; sudo -S service \$FORGE_PHP_FPM reload ) 9>/tmp/fpmlock\\n\\nif [ -f artisan ]; then\\n php artisan key:generate\\n php artisan migrate:fresh --seed --force\\nfi\\n\\nphp artisan queue:restart\\nphp artisan storage:link\\nnpm ci && npm run build\" | |
}") | |
echo "Deploy Script Update Response: $DEPLOY_SCRIPT_UPDATE_RESPONSE" | |
- name: Set Environment Variables | |
run: | | |
# Concatenate all secrets into one command string | |
COMMAND_TO_RUN="echo -e '\n# Appended by GitHub Actions\n" | |
SECRETS=( | |
"APP_ENV=dev" | |
"APP_DEBUG=true" | |
"DB_DATABASE=$PR_NUMBER" | |
) | |
for secret in "${SECRETS[@]}"; do | |
COMMAND_TO_RUN+="${secret}\\n" | |
done | |
COMMAND_TO_RUN+="' >> .env" | |
# Send the command to the server | |
SET_SECRET_RESPONSE=$(curl -s -X POST "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/commands" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Accept: application/json" \ | |
-H "Content-Type: application/json" \ | |
-d "{\"command\":\"$COMMAND_TO_RUN\"}") | |
# Extract the command ID | |
COMMAND_ID=$(echo "$SET_SECRET_RESPONSE" | jq -r '.command.id') | |
if [[ "$COMMAND_ID" == "null" || -z "$COMMAND_ID" ]]; then | |
echo "Failed to extract command ID." | |
exit 1 | |
fi | |
# Wait for the command to finish | |
COMMAND_STATUS="running" | |
while [[ "$COMMAND_STATUS" != "finished" ]]; do | |
COMMAND_STATUS_RESPONSE=$(curl -s -X GET "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/commands/$COMMAND_ID" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Accept: application/json") | |
COMMAND_STATUS=$(echo "$COMMAND_STATUS_RESPONSE" | jq -r '.command.status') | |
if [[ "$COMMAND_STATUS" != "finished" ]]; then | |
echo "Waiting for command ID $COMMAND_ID to finish..." | |
sleep 10 | |
fi | |
done | |
echo "Command completed successfully." | |
- name: Trigger Deployment | |
run: | | |
DEPLOY_TRIGGER_RESPONSE=$(curl -s -X POST "$SITE_URL/$SITE_ID/deployment/deploy" -H "Authorization: Bearer $FORGE_API_TOKEN" -H "Accept: application/json" -H "Content-Type: application/json") | |
DEPLOYMENT_STATUS="queued" | |
while [ "$DEPLOYMENT_STATUS" != "null" ]; do | |
echo "Checking deployment status..." | |
DEPLOY_STATUS_RESPONSE=$(curl -s -X GET "$SITE_URL/$SITE_ID" -H "Authorization: Bearer $FORGE_API_TOKEN" -H "Accept: application/json") | |
DEPLOYMENT_STATUS=$(echo "$DEPLOY_STATUS_RESPONSE" | jq -r '.site.deployment_status') | |
if [ "$DEPLOYMENT_STATUS" != "null" ]; then | |
if [ "$DEPLOYMENT_STATUS" == "error" ]; then | |
echo "Deployment failed with status: $DEPLOYMENT_STATUS" | |
exit 1 | |
fi | |
echo "Deployment status: $DEPLOYMENT_STATUS. Waiting for deployment to complete..." | |
sleep 30 | |
fi | |
done | |
- name: Create Queue Worker | |
run: | | |
QUEUE_WORKER_RESPONSE=$(curl -s -X POST "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/workers" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json" \ | |
-d '{ | |
"connection": "database", | |
"timeout": 0, | |
"sleep": 0, | |
"tries": null, | |
"processes": 1, | |
"stopwaitsecs": 600, | |
"daemon": true, | |
"force": false, | |
"php_version": "php" | |
}') | |
QUEUE_WORKER_ID=$(echo "$QUEUE_WORKER_RESPONSE" | jq -r '.worker.id') | |
if [[ "$QUEUE_WORKER_ID" == "null" || -z "$QUEUE_WORKER_ID" ]]; then | |
echo "Failed to create queue worker." | |
exit 1 | |
fi | |
# Wait for the queue worker to be installed | |
QUEUE_WORKER_STATUS="installing" | |
while [[ "$QUEUE_WORKER_STATUS" != "installed" ]]; do | |
QUEUE_WORKER_STATUS_RESPONSE=$(curl -s -X GET "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/workers/$QUEUE_WORKER_ID" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Accept: application/json") | |
QUEUE_WORKER_STATUS=$(echo "$QUEUE_WORKER_STATUS_RESPONSE" | jq -r '.worker.status') | |
if [[ "$QUEUE_WORKER_STATUS" != "installed" ]]; then | |
echo "Waiting for queue worker ID $QUEUE_WORKER_ID to be installed..." | |
sleep 10 | |
fi | |
done | |
echo "Queue worker created successfully." | |
- name: Provision Let's Encrypt SSL Certificate | |
id: ssl_provision | |
run: | | |
SSL_RESPONSE=$(curl -s -X POST "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/certificates/letsencrypt" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json" \ | |
-d "{\"domains\": [\"$PR_NUMBER.dev.example.com\"]}") | |
echo "$SSL_RESPONSE" | |
CERT_ID=$(echo "$SSL_RESPONSE" | jq -r '.certificate.id') | |
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV | |
echo "::set-output name=CERT_ID::$CERT_ID" | |
- name: Check Certificate Installation Status | |
if: steps.ssl_provision.outputs.CERT_ID != null | |
run: | | |
CERT_INSTALL_STATUS="installing" | |
while [ "$CERT_INSTALL_STATUS" != "installed" ]; do | |
echo "Checking certificate installation status..." | |
CERT_STATUS_RESPONSE=$(curl -s -X GET "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/certificates/$CERT_ID" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json") | |
CERT_INSTALL_STATUS=$(echo "$CERT_STATUS_RESPONSE" | jq -r '.certificate.status') | |
if [ "$CERT_INSTALL_STATUS" != "installed" ]; then | |
echo "Certificate status: $CERT_INSTALL_STATUS. Waiting for installation to complete..." | |
sleep 30 | |
fi | |
done | |
- name: Activate SSL Certificate | |
if: steps.ssl_provision.outputs.CERT_ID != null | |
run: | | |
ACTIVATE_RESPONSE=$(curl -s -X POST "https://forge.laravel.com/api/v1/servers/$FORGE_SERVER_ID/sites/$SITE_ID/certificates/$CERT_ID/activate" \ | |
-H "Authorization: Bearer $FORGE_API_TOKEN" \ | |
-H "Content-Type: application/json" \ | |
-H "Accept: application/json") | |
echo "Activate SSL Response: $ACTIVATE_RESPONSE" | |
echo "Certificate installation complete." | |
- name: Comment on PR | |
uses: actions/github-script@v5 | |
with: | |
github-token: ${{secrets.GITHUB_TOKEN}} | |
script: | | |
const prNumber = context.payload.pull_request.number; | |
const repository = context.repo; | |
github.rest.issues.createComment({ | |
owner: repository.owner, | |
repo: repository.repo, | |
issue_number: prNumber, | |
body: 'Your new environment is ready! :tada: Visit it at: https://' + prNumber + '.dev.example.com' | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment