Skip to content

Commit 56c53a0

Browse files
committed
Apply generated cpflow GitHub Actions flow
1 parent 2f7d333 commit 56c53a0

24 files changed

Lines changed: 1473 additions & 1137 deletions

.controlplane/readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ _If you need a free demo account for Control Plane (no CC required), you can con
66

77
---
88

9-
Check [how the `cpflow` gem (this project) is used in the Github actions](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.github/actions/deploy-to-control-plane/action.yml).
9+
Check [how the `cpflow` gem is used in the generated GitHub Actions flow](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.github/actions/cpflow-build-docker-image/action.yml).
1010
Here is a brief [video overview](https://www.youtube.com/watch?v=llaQoAV_6Iw).
1111

1212
---
@@ -385,6 +385,6 @@ The system uses Control Plane's infrastructure to manage these deployments, with
385385
### Workflow for Developing Github Actions for Review Apps
386386

387387
1. Create a PR with changes to the Github Actions workflow
388-
2. Make edits to file such as `.github/actions/deploy-to-control-plane/action.yml`
388+
2. Make edits to files such as `.github/actions/cpflow-build-docker-image/action.yml` or `.github/workflows/cpflow-deploy-review-app.yml`
389389
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)
390390
4. Check the Github Actions tab in the PR to see the status of the workflow

.controlplane/shakacode-team.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Deployments are handled by Control Plane configuration in this repo and GitHub A
1414
- [Staging App](https://staging.reactrails.com/)
1515

1616
### Production Environment
17-
- **Manual**: Run the [promote-staging-to-production workflow](https://github.com/shakacode/react-webpack-rails-tutorial/actions/workflows/promote-staging-to-production.yml) on GitHub
17+
- **Manual**: Run the [cpflow-promote-staging-to-production workflow](https://github.com/shakacode/react-webpack-rails-tutorial/actions/workflows/cpflow-promote-staging-to-production.yml) on GitHub
1818
- **URLs**:
1919
- [Control Plane Console - Production](https://console.cpln.io/console/org/shakacode-open-source-examples-production/gvc/react-webpack-rails-tutorial-production/workload/rails/-info)
2020
- [Production App](https://reactrails.com/)

.github/actions/build-docker-image/action.yml

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: Build Docker Image
2+
description: Builds and pushes the app image for a Control Plane workload
3+
4+
inputs:
5+
app_name:
6+
description: Name of the application
7+
required: true
8+
org:
9+
description: Control Plane organization name
10+
required: true
11+
commit:
12+
description: Commit SHA to tag the image with
13+
required: true
14+
pr_number:
15+
description: Pull request number for status messaging
16+
required: false
17+
docker_build_extra_args:
18+
description: Optional newline-delimited extra docker build tokens. Use key=value forms like --build-arg=FOO=bar.
19+
required: false
20+
docker_build_ssh_key:
21+
description: Optional private SSH key used for Docker builds that fetch private dependencies with RUN --mount=type=ssh
22+
required: false
23+
docker_build_ssh_known_hosts:
24+
description: Optional SSH known_hosts entries used with docker_build_ssh_key. Defaults to pinned GitHub.com host keys.
25+
required: false
26+
27+
runs:
28+
using: composite
29+
steps:
30+
# Keep SSH key handling in a dedicated step so DOCKER_BUILD_SSH_KEY is never present
31+
# in the main build step's environment. ACTIONS_STEP_DEBUG=true dumps env before any
32+
# command runs, so keeping the key out of env there avoids even admin-triggered exposure.
33+
- name: Prepare SSH agent for Docker build
34+
if: ${{ inputs.docker_build_ssh_key != '' }}
35+
shell: bash
36+
env:
37+
# Pass the key via env so the file write is a single printf call rather than a
38+
# heredoc with a fixed terminator (a heredoc would silently truncate the key if
39+
# any line of the key value happened to match the terminator). Scope is still
40+
# this step only — the build step below does not receive DOCKER_BUILD_SSH_KEY.
41+
DOCKER_BUILD_SSH_KEY: ${{ inputs.docker_build_ssh_key }}
42+
DOCKER_BUILD_SSH_KNOWN_HOSTS: ${{ inputs.docker_build_ssh_known_hosts }}
43+
run: |
44+
set -euo pipefail
45+
46+
umask 077
47+
mkdir -p ~/.ssh
48+
chmod 700 ~/.ssh
49+
50+
if [[ -n "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" ]]; then
51+
printf '%s\n' "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" > ~/.ssh/known_hosts
52+
else
53+
printf '%s\n' \
54+
'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl' \
55+
'github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=' \
56+
'github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=' \
57+
> ~/.ssh/known_hosts
58+
fi
59+
chmod 600 ~/.ssh/known_hosts
60+
61+
printf '%s\n' "${DOCKER_BUILD_SSH_KEY}" > ~/.ssh/cpflow_build_key
62+
chmod 600 ~/.ssh/cpflow_build_key
63+
64+
- name: Build Docker image
65+
shell: bash
66+
env:
67+
APP_NAME: ${{ inputs.app_name }}
68+
COMMIT_SHA: ${{ inputs.commit }}
69+
CONTROL_PLANE_ORG: ${{ inputs.org }}
70+
DOCKER_BUILD_EXTRA_ARGS: ${{ inputs.docker_build_extra_args }}
71+
PR_NUMBER: ${{ inputs.pr_number }}
72+
run: |
73+
set -euo pipefail
74+
75+
PR_INFO=""
76+
docker_build_args=()
77+
78+
if [[ -n "${PR_NUMBER}" ]]; then
79+
PR_INFO=" for PR #${PR_NUMBER}"
80+
fi
81+
82+
if [[ -n "${DOCKER_BUILD_EXTRA_ARGS}" ]]; then
83+
while IFS= read -r arg; do
84+
arg="${arg%$'\r'}"
85+
[[ -n "${arg}" ]] || continue
86+
87+
if [[ "${arg}" =~ [[:space:]] ]]; then
88+
echo "docker_build_extra_args entries must be single docker-build tokens. " \
89+
"Use key=value forms like --build-arg=FOO=bar." >&2
90+
exit 1
91+
fi
92+
93+
docker_build_args+=("${arg}")
94+
done <<< "${DOCKER_BUILD_EXTRA_ARGS}"
95+
fi
96+
97+
if [[ -f "${HOME}/.ssh/cpflow_build_key" ]]; then
98+
eval "$(ssh-agent -s)"
99+
trap 'ssh-agent -k >/dev/null; rm -f "${HOME}/.ssh/cpflow_build_key"' EXIT
100+
ssh-add "${HOME}/.ssh/cpflow_build_key"
101+
docker_build_args+=("--ssh=default")
102+
fi
103+
104+
echo "🏗️ Building Docker image${PR_INFO} (commit ${COMMIT_SHA})..."
105+
cpflow build-image -a "${APP_NAME}" --commit="${COMMIT_SHA}" --org="${CONTROL_PLANE_ORG}" "${docker_build_args[@]}"
106+
echo "✅ Docker image build successful${PR_INFO} (commit ${COMMIT_SHA})"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Delete Control Plane App
2+
description: Deletes a Control Plane app and all associated resources
3+
4+
inputs:
5+
app_name:
6+
description: Name of the application to delete
7+
required: true
8+
cpln_org:
9+
description: Control Plane organization name
10+
required: true
11+
review_app_prefix:
12+
description: Prefix used for review app names
13+
required: true
14+
15+
runs:
16+
using: composite
17+
steps:
18+
- name: Delete application
19+
shell: bash
20+
run: ${{ github.action_path }}/delete-app.sh
21+
env:
22+
APP_NAME: ${{ inputs.app_name }}
23+
CPLN_ORG: ${{ inputs.cpln_org }}
24+
REVIEW_APP_PREFIX: ${{ inputs.review_app_prefix }}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail
4+
5+
: "${APP_NAME:?APP_NAME environment variable is required}"
6+
: "${CPLN_ORG:?CPLN_ORG environment variable is required}"
7+
: "${REVIEW_APP_PREFIX:?REVIEW_APP_PREFIX environment variable is required}"
8+
9+
expected_prefix="${REVIEW_APP_PREFIX}-"
10+
if [[ "$APP_NAME" != "${expected_prefix}"* ]]; then
11+
echo "❌ ERROR: refusing to delete an app outside the review app prefix" >&2
12+
echo "App name: $APP_NAME" >&2
13+
echo "Expected prefix: ${expected_prefix}" >&2
14+
exit 1
15+
fi
16+
17+
echo "🔍 Checking if application exists: $APP_NAME"
18+
# Contract this relies on from `cpflow exists`:
19+
# - Exit status 0 → app exists (stdout may contain an informational banner).
20+
# - Exit status non-zero, no
21+
# recognizable error tokens → app does not exist; treat as a no-op success.
22+
# - Exit status non-zero with
23+
# tokens like "Double check
24+
# your org", "Unknown API
25+
# token format", "ERROR",
26+
# "Error:", "Traceback", or
27+
# "Net::" → a real failure; surface and exit 1.
28+
# TODO: replace this string-matching with a structured signal once `cpflow exists` exposes one
29+
# (e.g. a distinct exit code for "not found" vs. API/auth errors, or `cpflow exists --json`).
30+
# Until then, keep this list in sync if `cpflow exists` starts emitting new error patterns —
31+
# any unmatched error string would otherwise be silently treated as "app not found".
32+
exists_output=""
33+
if ! exists_output="$(cpflow exists -a "$APP_NAME" --org "$CPLN_ORG" 2>&1)"; then
34+
case "$exists_output" in
35+
*"Double check your org"*|*"Unknown API token format"*|*"ERROR"*|*"Error:"*|*"Traceback"*|*"Net::"*)
36+
echo "❌ ERROR: failed to determine whether application exists: $APP_NAME" >&2
37+
printf '%s\n' "$exists_output" >&2
38+
exit 1
39+
;;
40+
esac
41+
42+
if [[ -n "$exists_output" ]]; then
43+
printf '%s\n' "$exists_output"
44+
fi
45+
46+
echo "⚠️ Application does not exist: $APP_NAME"
47+
exit 0
48+
fi
49+
50+
if [[ -n "$exists_output" ]]; then
51+
printf '%s\n' "$exists_output"
52+
fi
53+
54+
echo "🗑️ Deleting application: $APP_NAME"
55+
cpflow delete -a "$APP_NAME" --org "$CPLN_ORG" --yes
56+
57+
echo "✅ Successfully deleted application: $APP_NAME"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Setup Control Plane Environment
2+
description: Sets up Ruby, installs the Control Plane CLI and cpflow gem, and configures a default profile
3+
4+
inputs:
5+
token:
6+
description: Control Plane token
7+
required: true
8+
org:
9+
description: Control Plane organization
10+
required: true
11+
ruby_version:
12+
description: >-
13+
Ruby version used for cpflow. When empty (the default), ruby/setup-ruby auto-detects
14+
from .ruby-version, .tool-versions, or the Gemfile.
15+
required: false
16+
default: ""
17+
cpln_cli_version:
18+
# Bump this default when a new @controlplane/cli release lands that you want to roll out.
19+
description: "@controlplane/cli version"
20+
required: false
21+
default: "3.3.1"
22+
cpflow_version:
23+
description: cpflow gem version
24+
required: false
25+
default: "4.2.0"
26+
27+
runs:
28+
using: composite
29+
steps:
30+
- name: Set up Ruby
31+
uses: ruby/setup-ruby@v1
32+
with:
33+
ruby-version: ${{ inputs.ruby_version }}
34+
35+
- name: Install Control Plane CLI and cpflow gem
36+
shell: bash
37+
run: |
38+
set -euo pipefail
39+
40+
sudo npm install -g @controlplane/cli@${{ inputs.cpln_cli_version }}
41+
cpln --version
42+
43+
gem install cpflow -v ${{ inputs.cpflow_version }}
44+
cpflow --version
45+
46+
- name: Setup Control Plane profile and registry login
47+
shell: bash
48+
env:
49+
# Pass the token via CPLN_TOKEN so cpln picks it up from the environment
50+
# rather than `--token`, which would leak it into /proc/<pid>/cmdline and ps output.
51+
CPLN_TOKEN: ${{ inputs.token }}
52+
ORG: ${{ inputs.org }}
53+
run: |
54+
set -euo pipefail
55+
56+
if [[ -z "$CPLN_TOKEN" ]]; then
57+
echo "Error: Control Plane token not provided" >&2
58+
exit 1
59+
fi
60+
61+
if [[ -z "$ORG" ]]; then
62+
echo "Error: Control Plane organization not provided" >&2
63+
exit 1
64+
fi
65+
66+
create_output=""
67+
if ! create_output="$(cpln profile create default --org "$ORG" 2>&1)"; then
68+
if ! echo "$create_output" | grep -qi "already exists"; then
69+
echo "$create_output" >&2
70+
exit 1
71+
fi
72+
fi
73+
74+
cpln profile update default --org "$ORG"
75+
cpln image docker-login --org "$ORG"

.github/actions/delete-control-plane-app/action.yml

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

.github/actions/delete-control-plane-app/delete-app.sh

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

0 commit comments

Comments
 (0)