Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5a01718

Browse files
authoredJan 26, 2025··
Improvements to deployments (#615)
Improve deployments and CI/CD workflows Summary: Introduced new GitHub Actions workflows for Docker image building and deployments. Enhanced Control Plane CLI and environment setup configurations. Streamlined concurrency management and deployment status tracking. Added new scripts and environment variables for commit tracking and deployment actions. Added documentation for CI automation and Review Apps.
1 parent 005eab7 commit 5a01718

20 files changed

+1010
-138
lines changed
 

‎.controlplane/Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
ARG RUBY_VERSION=3.3.4
33
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
44

5+
# Current commit hash environment variable
6+
ARG GIT_COMMIT
7+
ENV GIT_COMMIT_SHA=${GIT_COMMIT}
8+
59
# Install packages needed to build gems and node modules
610
RUN apt-get update -qq && \
711
apt-get install --no-install-recommends -y build-essential curl git libpq-dev libvips node-gyp pkg-config python-is-python3
@@ -76,7 +80,3 @@ ENTRYPOINT ["./.controlplane/entrypoint.sh"]
7680
# Default args to pass to the entry point that can be overridden
7781
# For Kubernetes and ControlPlane, these are the "workload args"
7882
CMD ["./bin/rails", "server"]
79-
80-
# Current commit hash environment variable
81-
ARG GIT_COMMIT_SHA
82-
ENV GIT_COMMIT_SHA=${GIT_COMMIT_SHA}

‎.controlplane/readme.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,32 @@ cpflow build-image -a $APP_NAME --commit ABCD
123123
### `entrypoint.sh`
124124
- waits for Postgres and Redis to be available
125125
- runs `rails db:prepare` to create/seed or migrate the database
126+
127+
## CI Automation, Review Apps and Staging
128+
129+
_Note, some of the URL references are internal for the ShakaCode team._
130+
131+
Review Apps (deployment of apps based on a PR) are done via Github Actions.
132+
133+
The review apps work by creating isolated deployments for each branch through this automated process. When a branch is pushed, the action:
134+
135+
1. Sets up the necessary environment and tools
136+
2. Creates a unique deployment for that branch if it doesn't exist
137+
3. Builds a Docker image tagged with the branch's commit SHA
138+
4. Deploys this image to Control Plane with its own isolated environment
139+
140+
This allows teams to:
141+
- Preview changes in a production-like environment
142+
- Test features independently
143+
- Share working versions with stakeholders
144+
- Validate changes before merging to main branches
145+
146+
The system uses Control Plane's infrastructure to manage these deployments, with each branch getting its own resources as defined in the controlplane.yml configuration.
147+
148+
149+
### Workflow for Developing Github Actions for Review Apps
150+
151+
1. Create a PR with changes to the Github Actions workflow
152+
2. Make edits to file such as `.github/actions/deploy-to-control-plane/action.yml`
153+
3. Run a script like `ga .github && gc -m fixes && gp` to commit and push changes (ga = git add, gc = git commit, gp = git push)
154+
4. Check the Github Actions tab in the PR to see the status of the workflow

‎.controlplane/shakacode-team.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Internal Notes to the Shakacode Team
2+
3+
## Links
4+
5+
- [Control Plane Org for Staging and Review Apps](https://console.cpln.io/console/org/shakacode-open-source-examples-staging/-info)
6+
- [Control Plane Org for Deployed App](https://console.cpln.io/console/org/shakacode-open-source-examples/-info)

‎.dockerignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ dump.rdb
1919
.DS_Store
2020

2121
# Ignore bundle dependencies
22-
vendor/ruby
22+
vendor/bundle
23+
24+
# Ignore GitHub Actions and workflows
25+
.github/
2326

2427
# RVM gemset
2528
.ruby-gemset
@@ -45,6 +48,5 @@ yarn-debug.log*
4548
###################################################
4649
# Specific to .dockerignore
4750
.git/
48-
.github/
4951
spec/
5052
scripts/
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Build Docker Image
2+
description: 'Builds a Docker image for the application'
3+
4+
inputs:
5+
app_name:
6+
description: 'Name of the application'
7+
required: true
8+
org:
9+
description: 'Organization name'
10+
required: true
11+
commit:
12+
description: 'Commit SHA to tag the image with'
13+
required: true
14+
PR_NUMBER:
15+
description: 'PR number'
16+
required: true
17+
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Build Docker Image
22+
id: build
23+
shell: bash
24+
run: |
25+
echo "🏗️ Building Docker image for PR #${PR_NUMBER} (commit ${{ inputs.commit }})..."
26+
27+
if cpflow build-image -a "${{ inputs.app_name }}" --commit="${{ inputs.commit }}" --org="${{ inputs.org }}"; then
28+
echo "✅ Docker image build successful for PR #${PR_NUMBER} (commit ${{ inputs.commit }})"
29+
else
30+
echo "❌ Docker image build failed for PR #${PR_NUMBER} (commit ${{ inputs.commit }})"
31+
exit 1
32+
fi
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Delete Control Plane App
2+
description: 'Deletes a Control Plane application and all its resources'
3+
4+
inputs:
5+
app_name:
6+
description: 'Name of the application to delete'
7+
required: true
8+
org:
9+
description: 'Organization name'
10+
required: true
11+
12+
runs:
13+
using: "composite"
14+
steps:
15+
- name: Delete Application
16+
shell: bash
17+
run: ${{ github.action_path }}/../deploy-to-control-plane/scripts/delete-app.sh
18+
env:
19+
APP_NAME: ${{ inputs.app_name }}
20+
CPLN_ORG: ${{ inputs.org }}
Lines changed: 72 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,91 @@
11
# Control Plane GitHub Action
22

3-
name: Deploy-To-Control-Plane
4-
description: 'Deploys both to staging and to review apps'
3+
name: Deploy to Control Plane
4+
description: 'Deploys an application to Control Plane'
55

66
inputs:
77
app_name:
8-
description: 'The name of the app to deploy'
8+
description: 'Name of the application'
99
required: true
10-
default:
1110
org:
12-
description: 'The org of the app to deploy'
11+
description: 'Organization name'
1312
required: true
14-
default:
13+
github_token:
14+
description: 'GitHub token'
15+
required: true
16+
wait_timeout:
17+
description: 'Timeout in seconds for waiting for workloads to be ready'
18+
required: false
19+
default: '900'
20+
21+
outputs:
22+
review_app_url:
23+
description: 'URL of the deployed application'
24+
value: ${{ steps.deploy.outputs.review_app_url }}
1525

1626
runs:
17-
using: 'composite'
27+
using: "composite"
1828
steps:
1929
- name: Setup Environment
2030
uses: ./.github/actions/setup-environment
2131

22-
- name: Set Short SHA
23-
id: vars
32+
- name: Get Commit SHA
33+
id: get_sha
2434
shell: bash
25-
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
26-
27-
# Caching step
28-
- uses: actions/cache@v2
29-
with:
30-
path: /tmp/.docker-cache
31-
key: ${{ runner.os }}-docker-${{ hashFiles('**/Dockerfile', '**/package.json', '**/yarn.lock') }}-${{ github.sha }}
32-
restore-keys: |
33-
${{ runner.os }}-docker-${{ hashFiles('**/Dockerfile', '**/package.json', '**/yarn.lock') }}
34-
${{ runner.os }}-docker-
35+
run: ${{ github.action_path }}/scripts/get-commit-sha.sh
36+
env:
37+
GITHUB_TOKEN: ${{ inputs.github_token }}
38+
PR_NUMBER: ${{ env.PR_NUMBER }}
3539

36-
- name: cpflow setup-app
37-
shell: bash
38-
run: |
39-
if ! cpflow exists -a ${{ inputs.app_name }} ; then
40-
cpflow setup-app -a ${{ inputs.app_name }}
41-
fi
42-
# Provision all infrastructure on Control Plane.
43-
# app react-webpack-rails-tutorial will be created per definition in .controlplane/controlplane.yml
44-
- name: cpflow build-image
45-
shell: bash
46-
run: |
47-
cpln image docker-login
48-
# Use BUILDKIT_PROGRESS=plain to get more verbose logging of the build
49-
# BUILDKIT_PROGRESS=plain cpflow build-image -a ${{ inputs.app_name }} --commit ${{steps.vars.outputs.sha_short}} --org ${{inputs.org}}
50-
cpflow build-image -a ${{ inputs.app_name }} --commit ${{steps.vars.outputs.sha_short}} --org ${{inputs.org}}
51-
# --cache /tmp/.docker-cache
5240
- name: Deploy to Control Plane
41+
id: deploy
5342
shell: bash
5443
run: |
55-
echo "Deploying to Control Plane"
56-
cpflow deploy-image -a ${{ inputs.app_name }} --run-release-phase --org ${{inputs.org}} --verbose
44+
echo "🚀 Deploying app for PR #${PR_NUMBER}..."
45+
46+
# Create temp file for output
47+
TEMP_OUTPUT=$(mktemp)
48+
trap 'rm -f "${TEMP_OUTPUT}"' EXIT
49+
50+
# Deploy the application and show output in real-time while capturing it
51+
if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then
52+
echo "❌ Deployment failed for PR #${PR_NUMBER}"
53+
echo "Error output:"
54+
cat "${TEMP_OUTPUT}"
55+
exit 1
56+
fi
57+
58+
# Extract app URL from captured output
59+
REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1)
60+
if [ -z "${REVIEW_APP_URL}" ]; then
61+
echo "❌ Failed to get app URL from deployment output"
62+
echo "Deployment output:"
63+
cat "${TEMP_OUTPUT}"
64+
exit 1
65+
fi
66+
67+
# Wait for all workloads to be ready
68+
WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}}
69+
if ! [[ "${WAIT_TIMEOUT}" =~ ^[0-9]+$ ]]; then
70+
echo "❌ Invalid timeout value: ${WAIT_TIMEOUT}"
71+
exit 1
72+
fi
73+
echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)"
74+
75+
# Use timeout command with ps:wait and show output in real-time
76+
if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then
77+
TIMEOUT_EXIT=$?
78+
if [ ${TIMEOUT_EXIT} -eq 124 ]; then
79+
echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds"
80+
else
81+
echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})"
82+
fi
83+
echo "Full output:"
84+
cat "${TEMP_OUTPUT}"
85+
exit 1
86+
fi
87+
88+
echo "✅ Deployment successful for PR #${PR_NUMBER}"
89+
echo "🌐 App URL: ${REVIEW_APP_URL}"
90+
echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT
91+
echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
3+
# Script to delete a Control Plane application
4+
# Required environment variables:
5+
# - APP_NAME: Name of the application to delete
6+
# - CPLN_ORG: Organization name
7+
8+
set -e
9+
10+
# Validate required environment variables
11+
: "${APP_NAME:?APP_NAME environment variable is required}"
12+
: "${CPLN_ORG:?CPLN_ORG environment variable is required}"
13+
14+
# Safety check: prevent deletion of production or staging apps
15+
if echo "$APP_NAME" | grep -iqE '(production|staging)'; then
16+
echo "❌ ERROR: Cannot delete apps containing 'production' or 'staging' in their name" >&2
17+
echo "🛑 This is a safety measure to prevent accidental deletion of production or staging environments" >&2
18+
echo " App name: $APP_NAME" >&2
19+
exit 1
20+
fi
21+
22+
# Check if app exists before attempting to delete
23+
echo "🔍 Checking if application exists: $APP_NAME"
24+
if ! cpflow exists -a "$APP_NAME"; then
25+
echo "⚠️ Application does not exist: $APP_NAME"
26+
exit 0
27+
fi
28+
29+
# Delete the application
30+
echo "🗑️ Deleting application: $APP_NAME"
31+
if ! cpflow delete -a "$APP_NAME" --force; then
32+
echo "❌ Failed to delete application: $APP_NAME" >&2
33+
exit 1
34+
fi
35+
36+
echo "✅ Successfully deleted application: $APP_NAME"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# This script handles the deployment to Control Plane and extracts the Rails URL
4+
#
5+
# Required environment variables:
6+
# - APP_NAME: Name of the application to deploy
7+
# - CPLN_ORG: Control Plane organization
8+
#
9+
# Optional environment variables:
10+
# - WAIT_TIMEOUT: Timeout in seconds for deployment (default: 900)
11+
# Must be a positive integer
12+
#
13+
# Outputs:
14+
# - rails_url: URL of the deployed Rails application
15+
16+
set -e
17+
18+
# Validate required environment variables
19+
: "${APP_NAME:?APP_NAME environment variable is required}"
20+
: "${CPLN_ORG:?CPLN_ORG environment variable is required}"
21+
22+
# Set and validate deployment timeout
23+
WAIT_TIMEOUT=${WAIT_TIMEOUT:-900}
24+
if ! [[ "${WAIT_TIMEOUT}" =~ ^[0-9]+$ ]]; then
25+
echo "❌ Invalid timeout value: ${WAIT_TIMEOUT}"
26+
exit 1
27+
fi
28+
29+
TEMP_OUTPUT=$(mktemp)
30+
trap 'rm -f "$TEMP_OUTPUT"' EXIT
31+
32+
# Deploy the application
33+
echo "🚀 Deploying to Control Plane (timeout: ${WAIT_TIMEOUT}s)"
34+
if timeout "$WAIT_TIMEOUT" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose | tee "$TEMP_OUTPUT"; then
35+
# Extract Rails URL from deployment output
36+
RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1)
37+
if [ -n "$RAILS_URL" ]; then
38+
echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT"
39+
echo "✅ Deployment successful"
40+
echo "🚀 Rails URL: $RAILS_URL"
41+
else
42+
echo "❌ Failed to extract Rails URL from deployment output"
43+
exit 1
44+
fi
45+
elif [ $? -eq 124 ]; then
46+
echo "❌ Deployment timed out after $WAIT_TIMEOUT seconds"
47+
exit 1
48+
else
49+
echo "❌ Deployment to Control Plane failed"
50+
exit 1
51+
fi
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
# This script retrieves the commit SHA for deployment
4+
# It handles both PR and direct branch deployments
5+
#
6+
# Required environment variables:
7+
# - PR_NUMBER: Pull request number (optional)
8+
# - GITHUB_TOKEN: GitHub token for API access
9+
#
10+
# Outputs:
11+
# - sha: Full commit SHA
12+
# - sha_short: Short (7 char) commit SHA
13+
14+
set -e
15+
16+
if [ -n "${PR_NUMBER}" ]; then
17+
# If PR_NUMBER is set, get the PR's head SHA
18+
if ! PR_SHA=$(gh pr view "${PR_NUMBER}" --json headRefOid --jq '.headRefOid'); then
19+
echo "Failed to get PR head SHA" >&2
20+
exit 1
21+
fi
22+
echo "sha=${PR_SHA}" >> "$GITHUB_OUTPUT"
23+
echo "sha_short=${PR_SHA:0:7}" >> "$GITHUB_OUTPUT"
24+
echo "Using PR head commit SHA: ${PR_SHA:0:7}"
25+
else
26+
# For direct branch deployments, use the current commit SHA
27+
if ! CURRENT_SHA=$(git rev-parse HEAD); then
28+
echo "Failed to get current SHA" >&2
29+
exit 1
30+
fi
31+
echo "sha=${CURRENT_SHA}" >> "$GITHUB_OUTPUT"
32+
echo "sha_short=${CURRENT_SHA:0:7}" >> "$GITHUB_OUTPUT"
33+
echo "Using branch commit SHA: ${CURRENT_SHA:0:7}"
34+
fi

‎.github/actions/setup-environment/action.yml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,27 @@ runs:
1414
- name: Install Control Plane CLI and cpflow gem
1515
shell: bash
1616
run: |
17-
sudo npm install -g @controlplane/cli@3.1.0
17+
sudo npm install -g @controlplane/cli@3.3.0
1818
cpln --version
19-
gem install cpflow -v 4.0.0
19+
gem install cpflow -v 4.1.0
2020
cpflow --version
2121
22-
- name: cpln profile
22+
- name: Setup Control Plane Profile
2323
shell: bash
2424
run: |
25-
cpln profile update default
25+
if [ -z "$CPLN_TOKEN" ]; then
26+
echo " Error: CPLN_TOKEN environment variable is not set"
27+
exit 1
28+
fi
29+
30+
if [ -z "$CPLN_ORG" ]; then
31+
echo " Error: CPLN_ORG environment variable is not set"
32+
exit 1
33+
fi
34+
35+
echo "Setting up Control Plane profile..."
36+
echo "Organization: $CPLN_ORG"
37+
cpln profile update default --org "$CPLN_ORG" --token "$CPLN_TOKEN"
38+
39+
echo "Setting up Docker login for Control Plane registry..."
40+
cpln image docker-login --org "$CPLN_ORG"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: Delete Review App
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
contents: read
9+
deployments: write
10+
pull-requests: write
11+
issues: write
12+
13+
env:
14+
CPLN_ORG: ${{ secrets.CPLN_ORG }}
15+
CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }}
16+
APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.issue.number }}
17+
PR_NUMBER: ${{ github.event.issue.number }}
18+
19+
jobs:
20+
debug-trigger:
21+
if: always()
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Debug Trigger Conditions
25+
env:
26+
EVENT_NAME: ${{ github.event_name }}
27+
IS_PR: ${{ toJSON(github.event.issue.pull_request) }}
28+
COMMENT: ${{ github.event.comment.body }}
29+
run: |
30+
echo "Debug information for delete-review-app command:"
31+
echo "Event name: $EVENT_NAME"
32+
echo "Is PR (raw): $IS_PR"
33+
echo "Comment body: $COMMENT"
34+
echo "Raw event payload:"
35+
echo '${{ toJSON(github.event) }}'
36+
37+
Process-Delete-Command:
38+
needs: debug-trigger
39+
if: |
40+
github.event_name == 'issue_comment' &&
41+
github.event.issue.pull_request &&
42+
github.event.comment.body == '/delete-review-app'
43+
runs-on: ubuntu-latest
44+
45+
steps:
46+
- uses: actions/checkout@v4
47+
48+
- name: Validate Required Secrets
49+
run: |
50+
missing_secrets=()
51+
for secret in "CPLN_TOKEN" "CPLN_ORG"; do
52+
if [ -z "${!secret}" ]; then
53+
missing_secrets+=("$secret")
54+
fi
55+
done
56+
57+
if [ ${#missing_secrets[@]} -ne 0 ]; then
58+
echo " Required secrets are not set: ${missing_secrets[*]}"
59+
exit 1
60+
fi
61+
62+
- name: Setup Environment
63+
uses: ./.github/actions/setup-environment
64+
65+
- name: Create Initial Delete Comment
66+
id: init-delete
67+
uses: actions/github-script@v7
68+
with:
69+
script: |
70+
const comment = await github.rest.issues.createComment({
71+
issue_number: process.env.PR_NUMBER,
72+
owner: context.repo.owner,
73+
repo: context.repo.repo,
74+
body: ' Starting app deletion...'
75+
});
76+
return { commentId: comment.data.id };
77+
78+
- name: Delete Review App
79+
uses: ./.github/actions/delete-control-plane-app
80+
with:
81+
app_name: ${{ env.APP_NAME }}
82+
org: ${{ env.CPLN_ORG }}
83+
github_token: ${{ secrets.GITHUB_TOKEN }}
84+
env:
85+
CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }}
86+
87+
- name: Update Delete Status
88+
if: always()
89+
uses: actions/github-script@v7
90+
with:
91+
script: |
92+
const success = '${{ job.status }}' === 'success';
93+
const prNumber = process.env.PR_NUMBER;
94+
const cpConsoleUrl = `https://console.cpln.io/org/${process.env.CPLN_ORG}/workloads/${process.env.APP_NAME}`;
95+
96+
const message = success
97+
? ' Review app for PR #' + prNumber + ' was successfully deleted'
98+
: [
99+
' Review app for PR #' + prNumber + ' failed to be deleted',
100+
'',
101+
'[Control Plane Console for Review App with PR #' + prNumber + '](' + cpConsoleUrl + ')'
102+
].join('\n');
103+
104+
await github.rest.issues.updateComment({
105+
owner: context.repo.owner,
106+
repo: context.repo.repo,
107+
comment_id: ${{ fromJSON(steps.init-delete.outputs.result).commentId }},
108+
body: message
109+
});

‎.github/workflows/deploy-to-control-plane-review.yml

Lines changed: 0 additions & 85 deletions
This file was deleted.

‎.github/workflows/deploy-to-control-plane-staging.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Control Plane GitHub Action
22

3-
name: Deploy-To-Control-Plane-Staging
3+
name: Deploy Main Branch to Control Plane Staging
44

55
# Controls when the workflow will run
66
on:
@@ -21,8 +21,10 @@ jobs:
2121
runs-on: ubuntu-latest
2222

2323
steps:
24-
- name: Check out the repo
25-
uses: actions/checkout@v2
24+
- uses: actions/checkout@v4
25+
with:
26+
fetch-depth: 0 # Fetch all history for proper SHA handling
27+
ref: master # Explicitly checkout master branch
2628

2729
- uses: ./.github/actions/deploy-to-control-plane
2830
with:

‎.github/workflows/deploy-to-control-plane.yml

Lines changed: 404 additions & 0 deletions
Large diffs are not rendered by default.

‎.github/workflows/help-command.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Show Help for Commands
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
issues: write
9+
pull-requests: write
10+
11+
jobs:
12+
debug-trigger:
13+
if: always()
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Debug Trigger Conditions
17+
env:
18+
EVENT_NAME: ${{ github.event_name }}
19+
IS_PR: ${{ toJSON(github.event.issue.pull_request) }}
20+
COMMENT: ${{ github.event.comment.body }}
21+
run: |
22+
echo "Debug information for help command:"
23+
echo "Event name: $EVENT_NAME"
24+
echo "Is PR (raw): $IS_PR"
25+
echo "Comment body: $COMMENT"
26+
echo "Raw event payload:"
27+
echo '${{ toJSON(github.event) }}'
28+
29+
show-help:
30+
needs: debug-trigger
31+
if: |
32+
github.event_name == 'issue_comment' &&
33+
github.event.issue.pull_request &&
34+
github.event.comment.body == '/help'
35+
runs-on: ubuntu-latest
36+
37+
steps:
38+
- name: Show Available Commands
39+
uses: actions/github-script@v7
40+
with:
41+
script: |
42+
const helpMessage = [
43+
'## 📚 Available Commands',
44+
'',
45+
'### `/deploy`',
46+
'Deploys your PR branch to a review environment on Control Plane.',
47+
'- Creates a new review app if one doesn\'t exist',
48+
'- Updates the existing review app if it already exists',
49+
'- Provides a unique URL to preview your changes',
50+
'- Shows build and deployment progress in real-time',
51+
'',
52+
'**Required Environment Variables:**',
53+
'- `CPLN_TOKEN`: Control Plane authentication token',
54+
'- `CPLN_ORG`: Control Plane organization name',
55+
'',
56+
'**Optional Configuration:**',
57+
'- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)',
58+
' - Must be a positive integer',
59+
' - Can be set in GitHub Actions variables',
60+
' - Applies to both deployment and workload readiness checks',
61+
'',
62+
'### `/delete-review-app`',
63+
'Deletes the review app associated with this PR.',
64+
'- Removes all resources from Control Plane',
65+
'- Helpful for cleaning up when you\'re done testing',
66+
'- Can be re-deployed later using `/deploy`',
67+
'',
68+
'**Required Environment Variables:**',
69+
'- `CPLN_TOKEN`: Control Plane authentication token',
70+
'- `CPLN_ORG`: Control Plane organization name',
71+
'',
72+
'### `/help`',
73+
'Shows this help message explaining available commands and configuration.',
74+
'',
75+
'---',
76+
'**Note:** These commands only work in pull request comments.',
77+
'',
78+
'**Environment Setup:**',
79+
'1. Set required secrets in your repository settings:',
80+
' - `CPLN_TOKEN`',
81+
' - `CPLN_ORG`',
82+
'2. Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout'
83+
].join('\n');
84+
85+
await github.rest.issues.createComment({
86+
owner: context.repo.owner,
87+
repo: context.repo.repo,
88+
issue_number: context.payload.issue.number,
89+
body: helpMessage
90+
});

‎.github/workflows/nightly-remove-stale-review-apps.yml

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,39 @@ jobs:
2121
- name: Setup Environment
2222
uses: ./.github/actions/setup-environment
2323

24-
- name: Run cleanup-stale-apps script
24+
- name: Get Stale PRs
25+
id: stale_prs
26+
uses: actions/github-script@v7
27+
with:
28+
script: |
29+
const thirtyDaysAgo = new Date();
30+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
31+
32+
const prs = await github.rest.pulls.list({
33+
owner: context.repo.owner,
34+
repo: context.repo.repo,
35+
state: 'closed',
36+
sort: 'updated',
37+
direction: 'desc'
38+
});
39+
40+
const stalePRs = prs.data
41+
.filter(pr => new Date(pr.updated_at) < thirtyDaysAgo)
42+
.map(pr => pr.number);
43+
44+
console.log('Found stale PRs:', stalePRs);
45+
return stalePRs;
46+
47+
- name: Delete Stale Review Apps
48+
if: ${{ steps.stale_prs.outputs.result != '[]' }}
2549
run: |
26-
cpflow cleanup-stale-apps -a qa-react-webpack-rails-tutorial -y
50+
for pr in $(echo "${{ steps.stale_prs.outputs.result }}" | jq -r '.[]'); do
51+
APP_NAME="qa-react-webpack-rails-tutorial-pr-$pr"
52+
echo "🗑️ Deleting stale review app for PR #$pr: $APP_NAME"
53+
${{ github.workspace }}/.github/actions/deploy-to-control-plane/scripts/delete-app.sh
54+
done
55+
env:
56+
APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ steps.stale_prs.outputs.result }}
2757

2858
- name: Run cleanup-images script
2959
run: |
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Promote Staging to Production
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
confirm_promotion:
7+
description: 'Type "promote" to confirm promotion of staging to production'
8+
required: true
9+
type: string
10+
11+
jobs:
12+
promote-to-production:
13+
runs-on: ubuntu-latest
14+
if: github.event.inputs.confirm_promotion == 'promote'
15+
16+
env:
17+
APP_NAME: react-webpack-rails-tutorial
18+
CPLN_ORG: ${{ secrets.CPLN_ORG }}
19+
UPSTREAM_TOKEN: ${{ secrets.STAGING_TOKEN }}
20+
21+
steps:
22+
- name: Checkout code
23+
uses: actions/checkout@v4
24+
25+
- name: Setup Environment
26+
uses: ./.github/actions/setup-environment
27+
env:
28+
CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }}
29+
30+
- name: Promote Staging to Production
31+
id: promote
32+
run: |
33+
echo "🚀 Starting promotion from staging to production..."
34+
35+
if ! cpflow promote-app-from-upstream -a "${APP_NAME}" -t "${UPSTREAM_TOKEN}" --org "${CPLN_ORG}"; then
36+
echo "❌ Failed to promote staging to production"
37+
exit 1
38+
fi
39+
40+
echo "✅ Successfully promoted staging to production"
41+
42+
- name: Create GitHub Release
43+
if: success()
44+
env:
45+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
run: |
47+
# Get the current date in YYYY-MM-DD format
48+
RELEASE_DATE=$(date '+%Y-%m-%d')
49+
50+
# Create a release tag
51+
RELEASE_TAG="production-${RELEASE_DATE}"
52+
53+
# Create GitHub release
54+
gh release create "${RELEASE_TAG}" \
55+
--title "Production Release ${RELEASE_DATE}" \
56+
--notes "🚀 Production deployment on ${RELEASE_DATE}"

‎.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dump.rdb
2323
.DS_Store
2424

2525
# Ignore bundle dependencies
26-
vendor/ruby
26+
vendor/bundle
2727

2828
# RVM gemset
2929
.ruby-gemset

‎CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
All notable changes to this project will be documented in this file.
33

44
See: [merged pull requests](https://github.com/shakacode/react-webpack-rails-tutorial/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Amerged).
5+
6+
## 2025-01-22
7+
Improvements to control-plane-flow implementation.
8+
9+
10+
511

612
## [2.1.0] - 2016-03-06
713
### Updated

0 commit comments

Comments
 (0)
Please sign in to comment.