Skip to content

Github actions fixes #618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading