Skip to content

Commit a436726

Browse files
committed
Update cpflow workflows to 5.0.0.rc.0
1 parent 3331332 commit a436726

14 files changed

Lines changed: 260 additions & 171 deletions

File tree

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ inputs:
2121
description: Optional private SSH key used for Docker builds that fetch private dependencies with RUN --mount=type=ssh
2222
required: false
2323
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; override if GitHub rotates keys or your build uses another SSH host.
24+
description: Optional SSH known_hosts entries used with docker_build_ssh_key. Defaults to pinned GitHub.com host keys.
2525
required: false
26+
working_directory:
27+
description: Directory containing the app .controlplane config and Docker build context
28+
required: false
29+
default: "."
2630

2731
runs:
2832
using: composite
@@ -48,10 +52,8 @@ runs:
4852
chmod 700 ~/.ssh
4953
5054
if [[ -n "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" ]]; then
51-
printf '%s\n' "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" > ~/.ssh/known_hosts
55+
printf '%s\n' "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" | tr -d '\r' > ~/.ssh/known_hosts
5256
else
53-
# GitHub.com host keys verified against GitHub's published keys on 2026-05-01.
54-
# Override docker_build_ssh_known_hosts if GitHub rotates keys again.
5557
printf '%s\n' \
5658
'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl' \
5759
'github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=' \
@@ -60,7 +62,7 @@ runs:
6062
fi
6163
chmod 600 ~/.ssh/known_hosts
6264
63-
printf '%s\n' "${DOCKER_BUILD_SSH_KEY}" > ~/.ssh/cpflow_build_key
65+
printf '%s\n' "${DOCKER_BUILD_SSH_KEY}" | tr -d '\r' > ~/.ssh/cpflow_build_key
6466
chmod 600 ~/.ssh/cpflow_build_key
6567
6668
- name: Build Docker image
@@ -71,20 +73,29 @@ runs:
7173
CONTROL_PLANE_ORG: ${{ inputs.org }}
7274
DOCKER_BUILD_EXTRA_ARGS: ${{ inputs.docker_build_extra_args }}
7375
PR_NUMBER: ${{ inputs.pr_number }}
76+
WORKING_DIRECTORY: ${{ inputs.working_directory }}
7477
run: |
7578
set -euo pipefail
7679
7780
PR_INFO=""
7881
docker_build_args=()
7982
ssh_agent_started=false
83+
build_ssh_prepped=false
8084
8185
cleanup_build_ssh() {
8286
if [[ "${ssh_agent_started}" == "true" ]]; then
8387
ssh-agent -k >/dev/null || true
8488
fi
8589
rm -f "${HOME}/.ssh/cpflow_build_key"
90+
# Only remove known_hosts if this action's prep step wrote it. On self-hosted
91+
# or reused runners we must not touch a user-managed file we did not create,
92+
# so the flag is set inside the same prep-detection branch below.
93+
if [[ "${build_ssh_prepped}" == "true" ]]; then
94+
rm -f "${HOME}/.ssh/known_hosts"
95+
fi
8696
}
8797
trap cleanup_build_ssh EXIT
98+
cd "${WORKING_DIRECTORY}"
8899
89100
if [[ -n "${PR_NUMBER}" ]]; then
90101
PR_INFO=" for PR #${PR_NUMBER}"
@@ -106,6 +117,9 @@ runs:
106117
fi
107118
108119
if [[ -f "${HOME}/.ssh/cpflow_build_key" ]]; then
120+
# Mark prep-step ownership so cleanup_build_ssh only removes known_hosts
121+
# when this action wrote it (see trap above).
122+
build_ssh_prepped=true
109123
eval "$(ssh-agent -s)"
110124
ssh_agent_started=true
111125
ssh-add "${HOME}/.ssh/cpflow_build_key"
@@ -115,10 +129,3 @@ runs:
115129
echo "🏗️ Building Docker image${PR_INFO} (commit ${COMMIT_SHA})..."
116130
cpflow build-image -a "${APP_NAME}" --commit="${COMMIT_SHA}" --org="${CONTROL_PLANE_ORG}" "${docker_build_args[@]}"
117131
echo "✅ Docker image build successful${PR_INFO} (commit ${COMMIT_SHA})"
118-
119-
- name: Remove SSH key
120-
if: ${{ always() }}
121-
shell: bash
122-
run: |
123-
# Defence in depth for cancellations or future refactors around the build trap.
124-
rm -f "${HOME}/.ssh/cpflow_build_key"

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,6 @@ if [[ "$APP_NAME" != "${expected_prefix}"* ]]; then
1414
exit 1
1515
fi
1616

17-
# Guard against a misconfigured REVIEW_APP_PREFIX that would otherwise match a
18-
# well-known shared environment. This intentionally rejects review-app prefixes
19-
# containing these word segments; failing closed is safer for deletion.
20-
if echo "$APP_NAME" | grep -iqE '(^|-)(production|staging)(-|$)'; then
21-
echo "❌ ERROR: refusing to delete an app whose name contains 'production' or 'staging'" >&2
22-
echo "App name: $APP_NAME" >&2
23-
exit 1
24-
fi
25-
2617
echo "🔍 Checking if application exists: $APP_NAME"
2718
exists_output=""
2819
set +e
@@ -33,7 +24,7 @@ set -e
3324
case "$exists_status" in
3425
0)
3526
;;
36-
2)
27+
3)
3728
if [[ -n "$exists_output" ]]; then
3829
printf '%s\n' "$exists_output"
3930
fi

.github/actions/cpflow-detect-release-phase/action.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ inputs:
77
app_name:
88
description: cpflow app name to inspect
99
required: true
10+
working_directory:
11+
description: Directory containing .controlplane/controlplane.yml
12+
required: false
13+
default: "."
1014

1115
outputs:
1216
flag:
@@ -21,14 +25,16 @@ runs:
2125
shell: bash
2226
env:
2327
APP_NAME: ${{ inputs.app_name }}
28+
WORKING_DIRECTORY: ${{ inputs.working_directory }}
2429
run: |
2530
set -euo pipefail
31+
cd "${WORKING_DIRECTORY}"
2632
2733
release_script="$(ruby - "${APP_NAME}" <<'RUBY'
2834
require "yaml"
2935
3036
app_name = ARGV.fetch(0)
31-
data = YAML.load_file(".controlplane/controlplane.yml", aliases: true)
37+
data = YAML.safe_load(File.read(".controlplane/controlplane.yml"), aliases: true)
3238
apps = data["apps"] || {}
3339
app_config = apps[app_name]
3440

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

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ inputs:
2929

3030
runs:
3131
using: composite
32+
# Third-party actions are pinned to floating major tags (`@v4`, `@v1`, `@v7`) rather than
33+
# immutable SHAs. SHA pinning is GitHub's stronger security recommendation, but for
34+
# generated templates that ship into many downstream repositories floating tags are
35+
# easier for users to keep current and Dependabot/Renovate already cover the SHA-pinning
36+
# workflow for repositories that opt in. Repositories with stricter supply-chain
37+
# requirements should replace each `uses: actions/...@vN` with the corresponding
38+
# immutable commit SHA.
3239
steps:
3340
- name: Set up Ruby
3441
uses: ruby/setup-ruby@v1
@@ -47,7 +54,7 @@ runs:
4754
# Override per-repo by setting `CPLN_CLI_VERSION` / `CPFLOW_VERSION` repo variables;
4855
# an empty input falls back to the action's pinned default below.
4956
default_cpln_cli_version="3.3.1"
50-
default_cpflow_version="4.2.0"
57+
default_cpflow_version="5.0.0.rc.0"
5158
5259
CPLN_CLI_VERSION="${CPLN_CLI_VERSION:-${default_cpln_cli_version}}"
5360
CPFLOW_VERSION="${CPFLOW_VERSION:-${default_cpflow_version}}"
@@ -83,22 +90,9 @@ runs:
8390
exit 1
8491
fi
8592
86-
# Persist the token because later cpflow/cpln steps read CPLN_TOKEN directly.
87-
# Use a randomized delimiter so a multiline token cannot terminate the heredoc early.
88-
delim="CPLN_TOKEN_$(openssl rand -hex 8)"
89-
{
90-
echo "CPLN_TOKEN<<${delim}"
91-
echo "${CPLN_TOKEN}"
92-
echo "${delim}"
93-
} >> "$GITHUB_ENV"
94-
95-
create_output=""
96-
if ! create_output="$(cpln profile create default --org "$ORG" 2>&1)"; then
97-
if ! echo "$create_output" | grep -qi "already exists"; then
98-
echo "$create_output" >&2
99-
exit 1
100-
fi
101-
fi
102-
93+
# `cpln profile update` lists `create` as an alias (cpln profile --help) and is
94+
# idempotent: it creates the profile if missing and updates it otherwise. Calling
95+
# update directly avoids parsing the CLI's "already exists" English error text,
96+
# which would silently swallow a real failure if the wording ever changed.
10397
cpln profile update default --org "$ORG"
10498
cpln image docker-login --org "$ORG"

.github/actions/cpflow-validate-config/action.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,23 @@ runs:
3939
missing=()
4040
while IFS= read -r entry; do
4141
entry="${entry%$'\r'}"
42-
entry="${entry#"${entry%%[![:space:]]*}"}"
43-
entry="${entry%"${entry##*[![:space:]]}"}"
42+
entry="${entry## }"
43+
entry="${entry%% }"
4444
[[ -z "${entry}" ]] && continue
4545
4646
type="${entry%%:*}"
4747
name="${entry#*:}"
4848
49+
# Reject names that are not plain SHELL_VAR identifiers before doing the
50+
# indirect lookup below. Without this guard, ${!name} would expand whatever
51+
# bash nameref/transformation a hand-edited generated workflow snuck in
52+
# (e.g. `BASH_FUNC_foo%%`). Callers today are the generated templates, but
53+
# the generated file lives in the user's repo and can be hand-edited.
54+
if [[ ! "${name}" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then
55+
echo "Invalid config entry name: ${name}" >&2
56+
exit 1
57+
fi
58+
4959
# Indirect bash lookup: reads the env var named by ${name} (e.g. CPLN_TOKEN_STAGING)
5060
# so the value never has to round-trip through workflow logs.
5161
if [[ -z "${!name:-}" ]]; then

.github/actions/cpflow-wait-for-health/action.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,15 @@ runs:
6161
for attempt in $(seq 1 "${CPFLOW_MAX_RETRIES}"); do
6262
echo "Health check attempt ${attempt}/${CPFLOW_MAX_RETRIES}"
6363
64-
if ! workload_json="$(cpln workload get "${CPFLOW_WORKLOAD_NAME}" --gvc "${CPFLOW_APP_NAME}" --org "${CPFLOW_ORG}" -o json 2>&1)"; then
64+
workload_stderr="$(mktemp)"
65+
if ! workload_json="$(cpln workload get "${CPFLOW_WORKLOAD_NAME}" --gvc "${CPFLOW_APP_NAME}" --org "${CPFLOW_ORG}" -o json 2>"${workload_stderr}")"; then
6566
echo "::error::Workload '${CPFLOW_WORKLOAD_NAME}' not found in GVC '${CPFLOW_APP_NAME}'. Set PRIMARY_WORKLOAD to the correct workload name." >&2
66-
printf '%s\n' "${workload_json}" >&2
67+
cat "${workload_stderr}" >&2
68+
rm -f "${workload_stderr}"
6769
echo "healthy=false" >> "$GITHUB_OUTPUT"
6870
exit 1
6971
fi
72+
rm -f "${workload_stderr}"
7073
7174
endpoint="$(echo "${workload_json}" | jq -r '.status.endpoint // empty')"
7275
if [[ -n "${endpoint}" ]]; then

.github/cpflow-help.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
- Creates the review app if it does not exist
77
- Builds the PR commit image
88
- Deploys the image and comments with the review URL
9-
- The comment must contain exactly `/deploy-review-app` and no other text
9+
- Comment body must be exactly `/deploy-review-app` no surrounding text, trailing whitespace, or trailing newline. The trigger uses an exact-equality match, so a comment like `please /deploy-review-app now` or `/deploy-review-app ` (with a trailing space) silently no-ops.
1010

1111
`/delete-review-app`
1212
- Deletes the review app when the PR is done
1313
- This also runs automatically when the PR closes
14-
- The comment must contain exactly `/delete-review-app` and no other text
14+
- Same exact-match rule as `/deploy-review-app`: the comment body must be exactly `/delete-review-app`.
1515

1616
## Repository secrets
1717

.github/workflows/cpflow-cleanup-stale-review-apps.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
steps:
2424
- name: Checkout repository
2525
uses: actions/checkout@v4
26+
with:
27+
persist-credentials: false
2628

2729
- name: Validate required secrets and variables
2830
uses: ./.github/actions/cpflow-validate-config
@@ -52,4 +54,3 @@ jobs:
5254
run: |
5355
set -euo pipefail
5456
cpflow cleanup-stale-apps -a "${REVIEW_APP_PREFIX}" --org "${CPLN_ORG_STAGING}" --yes
55-
echo "Stale review apps under prefix '${REVIEW_APP_PREFIX}' have been cleaned up." >> "$GITHUB_STEP_SUMMARY"

.github/workflows/cpflow-delete-review-app.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
name: Delete Review App
22

33
on:
4-
# SECURITY: pull_request_target is intentional for PR-close cleanup from forks.
5-
# This workflow must keep checking out trusted base-branch code only; do not
6-
# change checkout to a PR head ref without re-evaluating secret exposure.
74
pull_request_target:
85
types: [closed]
96
issue_comment:
@@ -54,6 +51,11 @@ jobs:
5451
# trusted base-branch code; keep them that way when changing this workflow.
5552
- name: Checkout repository
5653
uses: actions/checkout@v4
54+
with:
55+
# Delete only invokes `cpln`/`cpflow`; no git push happens, so drop the
56+
# GITHUB_TOKEN credential helper to keep the token out of .git/config under
57+
# `pull_request_target`, which has access to repository secrets.
58+
persist-credentials: false
5759

5860
- name: Validate required secrets and variables
5961
id: config
@@ -136,7 +138,9 @@ jobs:
136138
repo: context.repo.repo,
137139
deployment_id: deployment.id,
138140
state: "inactive",
139-
description: `Review app ${process.env.APP_NAME} was deleted`
141+
environment,
142+
log_url: process.env.WORKFLOW_URL,
143+
description: "Review app deleted"
140144
});
141145
}
142146

0 commit comments

Comments
 (0)