Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 3 additions & 2 deletions .github/cpflow-help.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ You asked for review app help. These commands are generated by [cpflow](https://
| `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. |
| `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. |
| `REVIEW_APP_PREFIX` | yes | Prefix for per-PR review app names (e.g. `review-app`). |
| `REVIEW_APP_DEPLOYING_ICON_URL` | optional | Custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. |
| `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. |
| `PRIMARY_WORKLOAD` | optional | Workload polled for health and rollback (defaults to `rails`). |
| `DOCKER_BUILD_EXTRA_ARGS` | optional | Newline-delimited extra docker build tokens (e.g. `--build-arg=FOO=bar`). |
Expand All @@ -60,14 +61,14 @@ You asked for review app help. These commands are generated by [cpflow](https://
<details>
<summary>Advanced: testing changes to generated workflows</summary>

When iterating on the generated workflow YAML on a PR branch, comment-triggered runs (`+review-app-deploy`, `+review-app-delete`, `+review-app-help`) execute the workflow code from the repository's default branch — not your PR branch. To exercise the top-level PR-branch workflow file before merging, dispatch the workflow manually with `gh`:
When iterating on the generated workflow YAML on a PR branch, comment-triggered runs (`+review-app-deploy`, `+review-app-delete`, `+review-app-help`) execute the workflow code from the repository's default branch — not your PR branch. To exercise the PR-branch workflow code before merging, dispatch the workflow manually with `gh`:

```sh
gh workflow run cpflow-deploy-review-app.yml --ref <your-pr-branch> -f pr_number=<pr-number>
gh workflow run cpflow-delete-review-app.yml --ref <your-pr-branch> -f pr_number=<pr-number>
gh workflow run cpflow-help-command.yml --ref <your-pr-branch> -f pr_number=<pr-number>
```

`workflow_dispatch` runs use the workflow file from the `--ref` you pass, but workflows that intentionally check out trusted local actions from the default branch will still load those local composite actions from the default branch before using secrets. Treat this as a partial smoke test for top-level workflow edits. For changes under `.github/actions/`, merge the generated fix to the default branch and rerun a real review-app deploy.
`workflow_dispatch` runs use the workflow file from the `--ref` you pass, so this is the supported way to test PR-branch workflow edits before merge. After merge, comment triggers go back to running the default-branch workflow code as usual.

</details>
Comment on lines 71 to 74

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Removed caveat about composite action testing limitation

The updated paragraph now says workflow_dispatch with --ref is "the supported way to test PR-branch workflow edits before merge," but it silently drops the original warning that workflows which check out local composite actions still load those actions from the default branch. A contributor iterating on files under .github/actions/ could misread this as confirmation that their changes are fully exercised by dispatch, only to discover post-merge that the composite action behaviour was unchanged during their test run. The removed sentence (For changes under .github/actions/, merge the generated fix to the default branch and rerun a real review-app deploy) is useful operational guidance worth preserving.

2 changes: 1 addition & 1 deletion .github/workflows/cpflow-cleanup-stale-review-apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
53 changes: 42 additions & 11 deletions .github/workflows/cpflow-delete-review-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ jobs:
timeout-minutes: 15

steps:
- name: React to delete command
if: github.event_name == 'issue_comment'
continue-on-error: true
uses: actions/github-script@v8
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
with:
script: |
try {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: "eyes"
});
} catch (error) {
if (error.status === 422) {
core.info("Delete command reaction already exists.");
} else {
throw error;
}
}

# pull_request_target is intentional: PR-close events from forks need access
# to staging secrets so this workflow can delete review apps and update PR
# comments. This checkout is safe because it does not set `ref:`; GitHub checks
Expand All @@ -49,7 +70,7 @@ jobs:
# the trust boundary. All local composite actions below are therefore loaded from
# trusted base-branch code; keep them that way when changing this workflow.
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
# Delete only invokes `cpln`/`cpflow`; no git push happens, so drop the
# GITHUB_TOKEN credential helper to keep the token out of .git/config under
Expand Down Expand Up @@ -81,7 +102,7 @@ jobs:

- name: Set workflow links
if: steps.config.outputs.ready == 'true'
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
Expand All @@ -94,14 +115,20 @@ jobs:
- name: Create initial PR comment
if: steps.config.outputs.ready == 'true'
id: create-comment
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const body = [
"## 🗑️ Deleting review app...",
"",
`_Removing the review app for PR #${process.env.PR_NUMBER}_`
].join("\n");

const comment = await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body: "🗑️ Deleting Control Plane review app..."
body
});
core.setOutput("comment-id", comment.data.id);

Expand All @@ -117,7 +144,7 @@ jobs:
# created the initial PR comment and workflow link env vars it updates.
- name: Finalize delete status
if: always() && steps.config.outputs.ready == 'true'
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
COMMENT_ID: ${{ steps.create-comment.outputs.comment-id }}
JOB_STATUS: ${{ job.status }}
Expand All @@ -127,16 +154,20 @@ jobs:
const success = process.env.JOB_STATUS === "success";
const body = success
? [
`✅ Review app for PR #${process.env.PR_NUMBER} is deleted`,
"## ✅ Review App Deleted",
"",
`[Open organization console](${process.env.CONSOLE_URL})`,
`[View workflow logs](${process.env.WORKFLOW_URL})`
`_Review app for PR #${process.env.PR_NUMBER} is deleted_`,
"",
`🎮 [Control Plane Console](${process.env.CONSOLE_URL})`,
`📋 [View Workflow Logs](${process.env.WORKFLOW_URL})`
].join("\n")
: [
`❌ Failed to delete review app for PR #${process.env.PR_NUMBER}`,
"## ❌ Failed to Delete Review App",
"",
`_Failed to delete review app for PR #${process.env.PR_NUMBER}_`,
"",
`[Open organization console](${process.env.CONSOLE_URL})`,
`[View workflow logs](${process.env.WORKFLOW_URL})`
`🎮 [Control Plane Console](${process.env.CONSOLE_URL})`,
`📋 [View Workflow Logs](${process.env.WORKFLOW_URL})`
].join("\n");

if (!Number.isFinite(commentId) || commentId <= 0) {
Expand Down
100 changes: 78 additions & 22 deletions .github/workflows/cpflow-deploy-review-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,29 @@ jobs:
timeout-minutes: 45

steps:
- name: React to deploy command
if: github.event_name == 'issue_comment'
continue-on-error: true
uses: actions/github-script@v8
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
with:
script: |
try {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: "rocket"
});
} catch (error) {
if (error.status === 422) {
core.info("Deploy command reaction already exists.");
} else {
throw error;
}
}

- name: Checkout trusted workflow sources
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
# Keep generated composite actions on the trusted base branch. The PR
# application code is checked out separately under ./app after source
Expand Down Expand Up @@ -164,7 +185,7 @@ jobs:

- name: Checkout PR commit
if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ env.PR_SHA }}
path: app
Expand Down Expand Up @@ -251,20 +272,26 @@ jobs:
- name: Create initial PR comment
if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
id: create-comment
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const body = [
"## 🚀 Starting deployment process...",
"",
`_Preparing review app deployment for PR #${process.env.PR_NUMBER}, commit ${process.env.PR_SHA}_`
].join("\n");

const result = await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body: "🚀 Starting Control Plane review app deployment..."
body
});
core.setOutput("comment-id", result.data.id);

- name: Set deployment links
if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
Expand All @@ -277,7 +304,7 @@ jobs:
- name: Initialize GitHub deployment
if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
id: init-deployment
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const deployment = await github.rest.repos.createDeployment({
Expand All @@ -302,7 +329,7 @@ jobs:

- name: Update PR comment with build status
if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
COMMENT_ID: ${{ steps.create-comment.outputs.comment-id }}
with:
Expand All @@ -316,9 +343,8 @@ jobs:
const body = [
`🏗️ Building Docker image for PR #${process.env.PR_NUMBER}, commit ${process.env.PR_SHA}`,
"",
`[View build logs](${process.env.WORKFLOW_URL})`,
"",
`[Open Control Plane console](${process.env.CONSOLE_URL})`
`📝 [View Build Logs](${process.env.WORKFLOW_URL})`,
`🎮 [Control Plane Console](${process.env.CONSOLE_URL})`
].join("\n");

await github.rest.issues.updateComment({
Expand All @@ -343,9 +369,10 @@ jobs:

- name: Update PR comment with deploy status
if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
COMMENT_ID: ${{ steps.create-comment.outputs.comment-id }}
DEPLOYING_ICON_URL: ${{ vars.REVIEW_APP_DEPLOYING_ICON_URL }}
with:
script: |
const commentId = Number(process.env.COMMENT_ID);
Expand All @@ -354,12 +381,37 @@ jobs:
return;
}

// Pinned to the commit that introduced this SVG for immutability.
// To update the icon: update the SVG, replace this SHA, and regenerate user workflows.
const DEFAULT_DEPLOYING_ICON_URL = "https://raw.githubusercontent.com/shakacode/control-plane-flow/7632313232b751aaa0bc55a122bf0615ff490345/docs/assets/cpflow-deploying.svg";
const configuredDeployingIconUrl = (process.env.DEPLOYING_ICON_URL || "").trim();
const isNone = configuredDeployingIconUrl.toLowerCase() === "none";
let deployingIconUrl = DEFAULT_DEPLOYING_ICON_URL;

if (configuredDeployingIconUrl && !isNone) {
try {
const parsedUrl = new URL(configuredDeployingIconUrl);
if (parsedUrl.protocol === "https:") {
deployingIconUrl = parsedUrl.href;
} else {
core.warning("Ignoring REVIEW_APP_DEPLOYING_ICON_URL because it must use https://.");
}
} catch {
core.warning("Ignoring REVIEW_APP_DEPLOYING_ICON_URL because it is not a valid URL.");
}
}

const deployingIcon = isNone
? "⏳"
: `<img src="${deployingIconUrl}" alt="Deploying" width="20" height="20" />`;

const body = [
"🚀 Deploying review app to Control Plane...",
"## 🚀 Deploying to Control Plane...",
"",
`[View deploy logs](${process.env.WORKFLOW_URL})`,
`${deployingIcon} **Waiting for deployment to be ready...**`,
"",
`[Open Control Plane console](${process.env.CONSOLE_URL})`
`📝 [View Deploy Logs](${process.env.WORKFLOW_URL})`,
`🎮 [Control Plane Console](${process.env.CONSOLE_URL})`
].join("\n");

await github.rest.issues.updateComment({
Expand Down Expand Up @@ -399,7 +451,7 @@ jobs:

- name: Finalize deployment status
if: always() && steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
COMMENT_ID: ${{ steps.create-comment.outputs.comment-id }}
DEPLOYMENT_ID: ${{ steps.init-deployment.outputs.result }}
Expand All @@ -426,19 +478,23 @@ jobs:
}

const successBody = [
"## Review app ready",
"## 🎉 Deploy Complete!",
"",
appUrl ? `[Open review app](${appUrl})` : "Review app deployed, but no endpoint URL was detected.",
appUrl ? `### [Open Review App](${appUrl})` : "Review app deployed, but no endpoint URL was detected.",
"",
`[Open Control Plane console](${process.env.CONSOLE_URL})`,
`[View workflow logs](${process.env.WORKFLOW_URL})`
`_Deployment successful for PR #${process.env.PR_NUMBER}, commit ${process.env.PR_SHA}_`,
"",
`🎮 [Control Plane Console](${process.env.CONSOLE_URL})`,
`📋 [View Completed Action Build and Deploy Logs](${process.env.WORKFLOW_URL})`
].join("\n");

const failureBody = [
`❌ Review app deployment failed for PR #${process.env.PR_NUMBER}`,
"## ❌ Review App Deployment Failed",
"",
`_Deployment failed for PR #${process.env.PR_NUMBER}, commit ${process.env.PR_SHA}_`,
"",
`[Open Control Plane console](${process.env.CONSOLE_URL})`,
`[View workflow logs](${process.env.WORKFLOW_URL})`
`🎮 [Control Plane Console](${process.env.CONSOLE_URL})`,
`📋 [View Failed Action Build and Deploy Logs](${process.env.WORKFLOW_URL})`
].join("\n");

if (!Number.isFinite(commentId) || commentId <= 0) {
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/cpflow-deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:

- name: Checkout repository
if: steps.check-branch.outputs.is_deployable == 'true'
uses: actions/checkout@v4
uses: actions/checkout@v6
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
with:
persist-credentials: false

Expand All @@ -79,7 +79,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
25 changes: 23 additions & 2 deletions .github/workflows/cpflow-help-command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,36 @@ jobs:
timeout-minutes: 5

steps:
- name: React to help command
if: github.event_name == 'issue_comment'
continue-on-error: true
uses: actions/github-script@v8
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
with:
script: |
try {
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: "eyes"
});
} catch (error) {
if (error.status === 422) {
core.info("Help command reaction already exists.");
} else {
throw error;
}
}

- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
# Help only reads `.github/cpflow-help.md`; no git push happens, so drop the
# GITHUB_TOKEN credential helper to keep the token out of .git/config.
persist-credentials: false

- name: Post help message
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const fs = require("fs");
Expand Down
Loading
Loading