Skip to content

Commit

Permalink
Enhance GitHub Actions workflows with new features and improvements (#…
Browse files Browse the repository at this point in the history
…618)

Summary:

Added validation for required secrets in deployment workflows.
Introduced multiple status update steps during deployment for better feedback.
Enhanced help command to reflect updated deployment command names.
Refined deployment triggers to focus on issue comments and push events.
Enhanced error handling and logging in deployment scripts.
Streamlined deployment process with checks for existing review apps.
Simplified workflow configuration for better readability and management.
  • Loading branch information
justin808 authored Jan 26, 2025
1 parent 4bf2c03 commit 151c527
Show file tree
Hide file tree
Showing 7 changed files with 501 additions and 295 deletions.
267 changes: 212 additions & 55 deletions .github/actions/deploy-to-control-plane/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,66 +26,223 @@ outputs:
runs:
using: "composite"
steps:
- name: Validate Required Secrets
shell: bash
run: |
missing_secrets=()
for secret in "CPLN_TOKEN" "CPLN_ORG"; do
if [ -z "${!secret}" ]; then
missing_secrets+=("$secret")
fi
done
if [ ${#missing_secrets[@]} -ne 0 ]; then
echo "Required secrets are not set: ${missing_secrets[*]}"
exit 1
fi
- name: Setup Environment
uses: ./.github/actions/setup-environment

- name: Get Commit SHA
id: get_sha
- name: Set shared functions
id: shared-functions
uses: actions/github-script@v7
with:
script: |
core.exportVariable('GET_CONSOLE_LINK', `
function getConsoleLink(prNumber) {
return ' [Control Plane Console for Review App with PR #' + prNumber + '](' +
'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')';
}
`);
- name: Initialize Deployment
id: init-deployment
uses: actions/github-script@v7
with:
script: |
eval(process.env.GET_CONSOLE_LINK);
async function getWorkflowUrl(runId) {
// Get the current job ID
const jobs = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId
});
const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress');
const jobId = currentJob?.id;
if (!jobId) {
console.log('Warning: Could not find current job ID');
return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
}
return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`;
}
// Create initial deployment comment
const comment = await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: process.env.PR_NUMBER,
body: ' Initializing deployment...'
});
// Create GitHub deployment
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
environment: 'review',
auto_merge: false,
required_contexts: []
});
const workflowUrl = await getWorkflowUrl(context.runId);
core.exportVariable('WORKFLOW_URL', workflowUrl);
core.exportVariable('COMMENT_ID', comment.data.id);
core.exportVariable('DEPLOYMENT_ID', deployment.data.id);
- name: Set commit hash
shell: bash
run: ${{ github.action_path }}/scripts/get-commit-sha.sh
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
PR_NUMBER: ${{ env.PR_NUMBER }}
run: |
FULL_COMMIT=$(git rev-parse HEAD)
echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV
- name: Deploy to Control Plane
id: deploy
- name: Update Status - Setting Up
uses: actions/github-script@v7
with:
script: |
eval(process.env.GET_CONSOLE_LINK);
const setupMessage = [
'🔧 Setting up Control Plane app...',
'',
' [View Setup Logs](' + process.env.WORKFLOW_URL + ')',
'',
getConsoleLink(process.env.PR_NUMBER)
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: process.env.COMMENT_ID,
body: setupMessage
});
- name: Setup Control Plane App
shell: bash
run: |
echo "🚀 Deploying app for PR #${PR_NUMBER}..."
# Create temp file for output
TEMP_OUTPUT=$(mktemp)
trap 'rm -f "${TEMP_OUTPUT}"' EXIT
# Deploy the application and show output in real-time while capturing it
if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then
echo "❌ Deployment failed for PR #${PR_NUMBER}"
echo "Error output:"
cat "${TEMP_OUTPUT}"
exit 1
fi
# Extract app URL from captured output
REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1)
if [ -z "${REVIEW_APP_URL}" ]; then
echo "❌ Failed to get app URL from deployment output"
echo "Deployment output:"
cat "${TEMP_OUTPUT}"
exit 1
fi
# Wait for all workloads to be ready
WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}}
if ! [[ "${WAIT_TIMEOUT}" =~ ^[0-9]+$ ]]; then
echo "❌ Invalid timeout value: ${WAIT_TIMEOUT}"
exit 1
echo "🔧 Checking if app exists..."
if ! cpflow exists -a ${{ inputs.app_name }} ; then
echo "📦 Setting up new Control Plane app..."
cpflow setup-app -a ${{ inputs.app_name }}
fi
echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)"
# Use timeout command with ps:wait and show output in real-time
if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then
TIMEOUT_EXIT=$?
if [ ${TIMEOUT_EXIT} -eq 124 ]; then
echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds"
else
echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})"
fi
echo "Full output:"
cat "${TEMP_OUTPUT}"
exit 1
fi
echo "✅ Deployment successful for PR #${PR_NUMBER}"
echo "🌐 App URL: ${REVIEW_APP_URL}"
echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT
echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV
- name: Update Status - Building
uses: actions/github-script@v7
with:
script: |
eval(process.env.GET_CONSOLE_LINK);
const buildingMessage = [
'🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + process.env.COMMIT_HASH,
'',
' [View Build Logs](' + process.env.WORKFLOW_URL + ')',
'',
getConsoleLink(process.env.PR_NUMBER)
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: process.env.COMMENT_ID,
body: buildingMessage
});
- name: Update Status - Deploying
uses: actions/github-script@v7
with:
script: |
eval(process.env.GET_CONSOLE_LINK);
const deployingMessage = [
'🚀 Deploying to Control Plane...',
'',
'⏳ Waiting for deployment to be ready...',
'',
' [View Deploy Logs](' + process.env.WORKFLOW_URL + ')',
'',
getConsoleLink(process.env.PR_NUMBER)
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: process.env.COMMENT_ID,
body: deployingMessage
});
- name: Deploy to Control Plane
id: deploy
shell: bash
run: ${{ github.action_path }}/scripts/deploy.sh
env:
APP_NAME: ${{ inputs.app_name }}
CPLN_ORG: ${{ inputs.org }}
WAIT_TIMEOUT: ${{ inputs.wait_timeout }}

- name: Update Status - Deployment Complete
if: always()
uses: actions/github-script@v7
with:
script: |
eval(process.env.GET_CONSOLE_LINK);
const prNumber = process.env.PR_NUMBER;
const appUrl = process.env.REVIEW_APP_URL;
const workflowUrl = process.env.WORKFLOW_URL;
const isSuccess = '${{ job.status }}' === 'success';
// Create GitHub deployment status
const deploymentStatus = {
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: process.env.DEPLOYMENT_ID,
state: isSuccess ? 'success' : 'failure',
environment_url: isSuccess ? appUrl : undefined,
log_url: workflowUrl,
environment: 'review'
};
await github.rest.repos.createDeploymentStatus(deploymentStatus);
// Define messages based on deployment status
const successMessage = [
'✅ Deployment complete for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH,
'',
'🌐 [Review App for PR #' + prNumber + '](' + appUrl + ')',
'',
' [View Completed Action Build and Deploy Logs](' + workflowUrl + ')',
'',
getConsoleLink(prNumber)
].join('\n');
const failureMessage = [
'❌ Deployment failed for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH,
'',
' [View Deployment Logs with Errors](' + workflowUrl + ')',
'',
getConsoleLink(prNumber)
].join('\n');
// Update the existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: process.env.COMMENT_ID,
body: isSuccess ? successMessage : failureMessage
});
51 changes: 33 additions & 18 deletions .github/actions/deploy-to-control-plane/scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

# This script handles the deployment to Control Plane and extracts the Rails URL
#
#
# Required environment variables:
# - APP_NAME: Name of the application to deploy
# - CPLN_ORG: Control Plane organization
Expand Down Expand Up @@ -31,21 +31,36 @@ trap 'rm -f "$TEMP_OUTPUT"' EXIT

# Deploy the application
echo "🚀 Deploying to Control Plane (timeout: ${WAIT_TIMEOUT}s)"
if timeout "$WAIT_TIMEOUT" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose | tee "$TEMP_OUTPUT"; then
# Extract Rails URL from deployment output
RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1)
if [ -n "$RAILS_URL" ]; then
echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT"
echo "✅ Deployment successful"
echo "🚀 Rails URL: $RAILS_URL"
else
echo "❌ Failed to extract Rails URL from deployment output"
exit 1
fi
elif [ $? -eq 124 ]; then
echo "❌ Deployment timed out after $WAIT_TIMEOUT seconds"
exit 1
else
echo "❌ Deployment to Control Plane failed"
exit 1
if ! timeout "${WAIT_TIMEOUT}" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose 2>&1 | tee "$TEMP_OUTPUT"; then
echo "❌ Deployment failed"
echo "Full output:"
cat "$TEMP_OUTPUT"
exit 1
fi

# Extract app URL from deployment output
RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1)
if [ -z "$RAILS_URL" ]; then
echo "❌ Failed to get app URL from deployment output"
echo "Full output:"
cat "$TEMP_OUTPUT"
exit 1
fi

# Wait for all workloads to be ready
echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)"
if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"$APP_NAME\"" 2>&1 | tee -a "$TEMP_OUTPUT"; then
TIMEOUT_EXIT=$?
if [ ${TIMEOUT_EXIT} -eq 124 ]; then
echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds"
else
echo "❌ Workloads did not become ready"
fi
echo "Full output:"
cat "$TEMP_OUTPUT"
exit 1
fi

echo "✅ Deployment successful"
echo "🌐 Rails URL: $RAILS_URL"
echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT"
36 changes: 18 additions & 18 deletions .github/workflows/add-comment-on-pr-creation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ jobs:
permissions:
pull-requests: write
steps:
name: Add GitHub Comment for review app instructions
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: [
"Hi 👋 Here are the commands available for this PR:",
"",
"- `/deploy-review-app`: Deploy your changes to a review environment",
"- `/delete-review-app`: Clean up the review environment when you're done",
"- `/help`: Show detailed information about all commands",
"",
"Use `/help` to see full documentation, including configuration options."
].join("\n")
});
- uses: actions/github-script@v7
name: Add GitHub Comment for review app instructions
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: [
"Hi 👋 Here are the commands available for this PR:",
"",
"- `/deploy-review-app`: Deploy your changes to a review environment",
"- `/delete-review-app`: Clean up the review environment when you're done",
"- `/help`: Show detailed information about all commands",
"",
"Use `/help` to see full documentation, including configuration options."
].join("\n")
});
Loading

0 comments on commit 151c527

Please sign in to comment.