Skip to content

[codex] Regenerate cpflow review app feedback#741

Merged
justin808 merged 3 commits into
masterfrom
jg-codex/animated-review-app-feedback
May 22, 2026
Merged

[codex] Regenerate cpflow review app feedback#741
justin808 merged 3 commits into
masterfrom
jg-codex/animated-review-app-feedback

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented May 22, 2026

Summary

Regenerates the generated cpflow review-app workflows after upstream shakacode/control-plane-flow PRs #303 and #304 merged.

This rolls out:

  • Node 24-compatible action versions (actions/checkout@v6, actions/github-script@v8) in generated cpflow workflows
  • immediate emoji reactions when +review-app-deploy, +review-app-delete, or +review-app-help commands are picked up
  • richer review-app status comment formatting
  • animated deploying icon support via REVIEW_APP_DEPLOYING_ICON_URL, with none as the text fallback
  • updated help docs for PR-branch workflow testing

I intentionally kept the existing downstream cpflow-setup-environment action unchanged because upstream PR #297 is still open; regenerating it from current upstream main would reintroduce literal ${{ vars.* }} examples in composite action descriptions.

Testing

  • git diff --cached --check
  • bin/conductor-exec ruby -e 'require "yaml"; paths = Dir[".github/workflows/cpflow-*.yml", ".github/actions/cpflow-*/*.yml", ".github/actions/cpflow-*/action.yml"].sort; paths.each { |path| YAML.load_file(path, aliases: true) }; puts "parsed #{paths.size} workflow/action YAML files"'
  • actionlint -ignore 'SC2129' .github/workflows/cpflow-cleanup-stale-review-apps.yml .github/workflows/cpflow-delete-review-app.yml .github/workflows/cpflow-deploy-review-app.yml .github/workflows/cpflow-deploy-staging.yml .github/workflows/cpflow-help-command.yml .github/workflows/cpflow-promote-staging-to-production.yml .github/workflows/cpflow-review-app-help.yml

After this merges to master, open or reuse a fresh test PR and comment +review-app-deploy; issue-comment workflows load from the default branch, so merging this PR first is the meaningful test path.


Note

Medium Risk
Switches the repo’s deploy/promote/review-app automation to reusable workflows from shakacode/control-plane-flow, changing critical CI/CD behavior and introducing an external pinned dependency. Risk is moderated by SHA pinning but failures/mismatches could break deployments or PR automation.

Overview
Refactors the generated cpflow-* GitHub Actions automation to delegate almost all logic to upstream reusable workflows in shakacode/control-plane-flow (pinned by SHA) with secrets: inherit, replacing the previously in-repo step implementations.

Removes the local composite actions/scripts that handled config validation, environment setup, Docker image builds (incl. SSH key handling), app deletion, release-phase detection, and health polling.

Updates .github/cpflow-help.md to document the new optional REVIEW_APP_DEPLOYING_ICON_URL variable and clarifies PR-branch workflow testing guidance; adjusts workflow permissions (notably promotion now requests contents: write).

Reviewed by Cursor Bugbot for commit 6b5760d. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added optional REVIEW_APP_DEPLOYING_ICON_URL to customize the animated deploying icon in review-app PR comments; set to none for a text fallback.
  • Documentation

    • Clarified testing guidance for workflow edits and comment-triggered review-app commands; updated recommended workflow-run commands and wording.
  • Chores

    • Consolidated CI: many jobs now delegate to centralized reusable workflows; several local composite actions and helper scripts were removed; adjusted some workflow permissions.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Multiple cpflow workflows replace inline job steps with wrapper jobs that call upstream reusable workflows (passing refs/inputs and inheriting secrets), and .github/cpflow-help.md adds REVIEW_APP_DEPLOYING_ICON_URL and clarifies workflow_dispatch testing with --ref and PR number usage.

Changes

Workflow delegation & help documentation

Layer / File(s) Summary
Delegate jobs to reusable workflows
.github/workflows/cpflow-cleanup-stale-review-apps.yml, .github/workflows/cpflow-delete-review-app.yml, .github/workflows/cpflow-deploy-review-app.yml, .github/workflows/cpflow-deploy-staging.yml, .github/workflows/cpflow-help-command.yml, .github/workflows/cpflow-promote-staging-to-production.yml, .github/workflows/cpflow-review-app-help.yml
Jobs previously implemented inline are converted into wrapper jobs that call upstream reusable workflows (shakacode/control-plane-flow/...) via uses:, with control_plane_flow_ref inputs and secrets: inherit; some top-level env/concurrency/permissions entries were removed or adjusted.
Help documentation: deploying icon & testing guidance
.github/cpflow-help.md
Adds REVIEW_APP_DEPLOYING_ICON_URL documentation describing icon customization and the none text fallback; rewrites "Advanced: testing changes to generated workflows" to show gh workflow run --ref ... -f pr_number=... usage and clarify workflow_dispatch testing behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A rabbit scampers, scripts in paw,
Reusable flows tidy CI law,
An icon URL winks on deploy,
Docs now guide the testing joy,
Hooray — the workflows hop with joy!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title '[codex] Regenerate cpflow review app feedback' is vague and does not clearly convey the main changes, which involve migrating workflows to reusable upstream workflows and removing local composite actions. Consider a more descriptive title such as 'Migrate cpflow workflows to upstream reusable workflows' or 'Regenerate cpflow workflows with reusable workflow delegation' to better reflect the primary structural change.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/animated-review-app-feedback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Review app commands

  • +review-app-deploy - create or redeploy this PR's review app.
  • +review-app-delete - delete this PR's review app and temporary resources.
  • +review-app-help - show setup details and workflow behavior.

For setup details, comment +review-app-help.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR regenerates cpflow-managed GitHub Actions workflows to pick up upstream shakacode/control-plane-flow changes from PRs #303 and #304. All workflows are updated with Node 24-compatible action versions (actions/checkout@v6, actions/github-script@v8), and the review-app workflows gain immediate emoji reactions on comment triggers, richer PR comment formatting with Markdown headers, and optional animated deploying-icon support via REVIEW_APP_DEPLOYING_ICON_URL.

  • Reaction steps (eyes / rocket) are added as the first step in each comment-triggered workflow, gated with if: github.event_name == 'issue_comment' and continue-on-error: true so manual dispatch runs are unaffected and reaction failures are non-blocking.
  • REVIEW_APP_DEPLOYING_ICON_URL support in cpflow-deploy-review-app.yml validates the configured URL with new URL() and enforces https: before using it, falling back to the pinned default SVG or when set to none.
  • cpflow-help.md simplifies the workflow_dispatch testing guidance but removes a caveat about composite actions still loading from the default branch during dispatch runs.

Confidence Score: 4/5

Safe to merge; changes are purely additive workflow improvements with no logic regressions introduced.

All workflow upgrades are mechanical version bumps or additive steps, and the new REVIEW_APP_DEPLOYING_ICON_URL feature includes https-only URL validation with a safe fallback. The one concern is that cpflow-help.md no longer tells contributors that composite actions still load from the default branch when testing via workflow_dispatch --ref, which could cause confusion during future workflow iteration.

.github/cpflow-help.md — the revised testing guidance section warrants a second look to decide whether the removed composite-action caveat should be restored.

Important Files Changed

Filename Overview
.github/cpflow-help.md Adds REVIEW_APP_DEPLOYING_ICON_URL variable docs and simplifies workflow-dispatch testing guidance, but removes the composite-action limitation caveat.
.github/workflows/cpflow-deploy-review-app.yml Adds immediate rocket-reaction step, upgrades checkout/github-script action versions, adds REVIEW_APP_DEPLOYING_ICON_URL support with https-only URL validation and proper fallback; comment formatting improved throughout.
.github/workflows/cpflow-delete-review-app.yml Adds eyes-reaction step on issue_comment, upgrades action versions (checkout@v4→v6, github-script@v7→v8), and improves PR comment formatting with headers and emoji links.
.github/workflows/cpflow-help-command.yml Adds eyes-reaction step on issue_comment and upgrades checkout@v4→v6 and github-script@v7→v8.
.github/workflows/cpflow-review-app-help.yml Upgrades github-script@v7→v8 and rewrites help message body with richer Markdown formatting using section headers per command.
.github/workflows/cpflow-cleanup-stale-review-apps.yml Single-line change: upgrades actions/checkout@v4 to @v6.
.github/workflows/cpflow-deploy-staging.yml Three checkout steps upgraded from actions/checkout@v4 to @v6; no logic changes.
.github/workflows/cpflow-promote-staging-to-production.yml Single-line change: upgrades actions/checkout@v4 to @v6.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[issue_comment event\nor workflow_dispatch] --> B{event is\nissue_comment?}
    B -- yes --> C[React to command\n👀 or 🚀\ncontinue-on-error: true]
    B -- no --> D[Skip reaction]
    C --> E[Checkout trusted\nworkflow sources\nactions/checkout@v6]
    D --> E
    E --> F[cpflow-setup-environment\ncomposite action]
    F --> G{config.outputs.ready\n== 'true'?}
    G -- no --> H[Stop - not configured]
    G -- yes --> I[Checkout PR commit\nactions/checkout@v6]
    I --> J[Build Docker image]
    J --> K[Create initial PR comment\ngithub-script@v8]
    K --> L[Deploy to Control Plane]
    L --> M{REVIEW_APP_DEPLOYING\n_ICON_URL configured?}
    M -- not set --> N[Use default pinned SVG URL]
    M -- 'none' --> O[Use ⏳ text icon]
    M -- valid https URL --> P[Use custom URL]
    M -- invalid URL --> N
    N --> Q[Update PR comment\nwith deploying icon]
    O --> Q
    P --> Q
    Q --> R[Wait for health check]
    R --> S{Deploy success?}
    S -- yes --> T[🎉 Deploy Complete!\nwith app URL]
    S -- no --> U[❌ Deployment Failed]
    T --> V[Finalize GitHub Deployment status]
    U --> V
Loading

Reviews (1): Last reviewed commit: "Regenerate cpflow review app feedback" | Re-trigger Greptile

Comment thread .github/cpflow-help.md
Comment on lines 71 to 74

`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>
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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
.github/workflows/cpflow-cleanup-stale-review-apps.yml (1)

25-25: ⚡ Quick win

Pin actions/checkout to an immutable commit SHA (when stricter supply-chain requirements apply).

This repo’s templates intentionally use floating major tags (e.g., actions/...@vN) and note that immutable SHA pinning should be applied by repositories with stricter supply-chain requirements. If that applies to your environment, replace actions/checkout@v6 with actions/checkout@<40-hex-commit-sha>.

Suggested change
-        uses: actions/checkout@v6
+        uses: actions/checkout@<FULL_LENGTH_SHA_FOR_V6> # v6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cpflow-cleanup-stale-review-apps.yml at line 25, Replace
the floating tag reference "actions/checkout@v6" with an immutable 40-character
commit SHA (e.g., "actions/checkout@<40-hex-commit-sha>") to pin the action;
locate the usage of actions/checkout in the workflow file (the line containing
actions/checkout@v6) and update it to the specific commit SHA you trust for
supply-chain security.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/cpflow-delete-review-app.yml:
- Line 47: The workflow is using mutable action tags; replace each mutable
`uses:` entry like `actions/github-script@v8` and `actions/checkout@v6` with
their corresponding immutable commit SHAs (e.g.,
`actions/github-script@<full-sha>` and `actions/checkout@<full-sha>`) while
preserving the trailing `# v8` / `# v6` comment; update all occurrences (the
three `actions/github-script@v8` entries and the `actions/checkout@v6` entry) to
the full SHA commits and verify the workflow still runs by committing the
updated `uses:` lines.

In @.github/workflows/cpflow-deploy-review-app.yml:
- Line 57: Replace mutable action tags with immutable commit SHAs: find every
usage of actions/github-script@v8 and actions/checkout@v6 and pin them to the
full 40-character commit SHA from the corresponding GitHub Actions repository
(e.g., the latest vetted commit on actions/github-script and actions/checkout),
updating each occurrence to use the commit SHA ref instead of the short tag;
ensure you update all instances of the symbols actions/github-script@v8 and
actions/checkout@v6 in the workflow so they consistently reference the exact
commit SHA.

In @.github/workflows/cpflow-deploy-staging.yml:
- Line 58: Three steps use the mutable tag actions/checkout@v6; locate each
checkout step (the uses: "actions/checkout@v6" occurrences) and replace the tag
with the full commit SHA for the actions/checkout repository (e.g. uses:
actions/checkout@<full-commit-sha>) so the workflow is pinned immutably; update
all three occurrences (the ones currently set to actions/checkout@v6) to their
corresponding commit SHAs and verify the SHAs are valid by copying them from the
actions/checkout GitHub repo.

In @.github/workflows/cpflow-help-command.yml:
- Line 38: The workflow currently pins actions using tag refs
'actions/github-script@v8' and 'actions/checkout@v6'; replace each tag ref with
the corresponding full commit SHA (the exact commit SHA for the version you
intend to use) so the uses entries become commit-pinned (e.g.,
'actions/github-script@<full-sha>' and 'actions/checkout@<full-sha>'). Update
every occurrence of those strings in the workflow so both instances are
replaced, and verify the workflow still runs against the chosen SHAs.

In @.github/workflows/cpflow-promote-staging-to-production.yml:
- Line 55: Replace the mutable tag "actions/checkout@v6" with the repository's
full immutable commit SHA for that action (e.g., "actions/checkout@<full-sha> #
v6") so the workflow is deterministic; locate the uses: actions/checkout@v6
entry in the workflow and update it to the full SHA while optionally keeping a
trailing comment with "v6" for human readability.

In @.github/workflows/cpflow-review-app-help.yml:
- Line 25: Replace the floating actions/github-script@v8 reference with an
immutable commit SHA (or digest) to avoid drift: locate the workflow step that
currently says "uses: actions/github-script@v8" and change it to the
corresponding actions/github-script@<commit-sha> (or @{digest}) pinned value,
ensuring you reference the official GitHub Actions repository commit you want to
lock to and update any related comment to document the chosen SHA.

---

Nitpick comments:
In @.github/workflows/cpflow-cleanup-stale-review-apps.yml:
- Line 25: Replace the floating tag reference "actions/checkout@v6" with an
immutable 40-character commit SHA (e.g., "actions/checkout@<40-hex-commit-sha>")
to pin the action; locate the usage of actions/checkout in the workflow file
(the line containing actions/checkout@v6) and update it to the specific commit
SHA you trust for supply-chain security.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 433adebd-013b-49d2-9126-d537e2dc4296

📥 Commits

Reviewing files that changed from the base of the PR and between 2af925b and 57897c1.

📒 Files selected for processing (8)
  • .github/cpflow-help.md
  • .github/workflows/cpflow-cleanup-stale-review-apps.yml
  • .github/workflows/cpflow-delete-review-app.yml
  • .github/workflows/cpflow-deploy-review-app.yml
  • .github/workflows/cpflow-deploy-staging.yml
  • .github/workflows/cpflow-help-command.yml
  • .github/workflows/cpflow-promote-staging-to-production.yml
  • .github/workflows/cpflow-review-app-help.yml

Comment thread .github/workflows/cpflow-delete-review-app.yml Outdated
Comment thread .github/workflows/cpflow-deploy-review-app.yml Outdated
Comment thread .github/workflows/cpflow-deploy-staging.yml Outdated
Comment thread .github/workflows/cpflow-help-command.yml Outdated
Comment thread .github/workflows/cpflow-promote-staging-to-production.yml Outdated
Comment thread .github/workflows/cpflow-review-app-help.yml Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.github/workflows/cpflow-cleanup-stale-review-apps.yml (1)

13-16: 🏗️ Heavy lift

Branch reference should be pinned to a SHA or release tag before production use.

All seven cpflow workflow files in this PR reference the reusable workflows at @jg-codex/reusable-github-actions, which is a mutable branch. While this appears intentional for testing the upstream changes from PRs #303/#304, ensure these references are updated to an immutable SHA or release tag before final merge to master for production stability.

The secrets: inherit pattern is acceptable here since the called workflows are from the same organization's trusted repository.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cpflow-cleanup-stale-review-apps.yml around lines 13 - 16,
The workflow references a mutable branch in the uses line ("uses:
shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@jg-codex/reusable-github-actions");
update that reference to an immutable identifier (a specific commit SHA or a
release tag) in this file and the other six cpflow workflow files so CI uses a
fixed version of shakacode/control-plane-flow; ensure you pick the exact SHA or
tag you tested against (or create a release), replace the branch suffix with
that SHA/tag, and keep "secrets: inherit" unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/cpflow-cleanup-stale-review-apps.yml:
- Around line 13-16: The workflow references a mutable branch in the uses line
("uses:
shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@jg-codex/reusable-github-actions");
update that reference to an immutable identifier (a specific commit SHA or a
release tag) in this file and the other six cpflow workflow files so CI uses a
fixed version of shakacode/control-plane-flow; ensure you pick the exact SHA or
tag you tested against (or create a release), replace the branch suffix with
that SHA/tag, and keep "secrets: inherit" unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f9ed0a80-9345-4890-84f4-a1a04c764b4a

📥 Commits

Reviewing files that changed from the base of the PR and between 57897c1 and 2829287.

📒 Files selected for processing (14)
  • .github/actions/cpflow-build-docker-image/action.yml
  • .github/actions/cpflow-delete-control-plane-app/action.yml
  • .github/actions/cpflow-delete-control-plane-app/delete-app.sh
  • .github/actions/cpflow-detect-release-phase/action.yml
  • .github/actions/cpflow-setup-environment/action.yml
  • .github/actions/cpflow-validate-config/action.yml
  • .github/actions/cpflow-wait-for-health/action.yml
  • .github/workflows/cpflow-cleanup-stale-review-apps.yml
  • .github/workflows/cpflow-delete-review-app.yml
  • .github/workflows/cpflow-deploy-review-app.yml
  • .github/workflows/cpflow-deploy-staging.yml
  • .github/workflows/cpflow-help-command.yml
  • .github/workflows/cpflow-promote-staging-to-production.yml
  • .github/workflows/cpflow-review-app-help.yml
💤 Files with no reviewable changes (7)
  • .github/actions/cpflow-setup-environment/action.yml
  • .github/actions/cpflow-wait-for-health/action.yml
  • .github/actions/cpflow-delete-control-plane-app/action.yml
  • .github/actions/cpflow-delete-control-plane-app/delete-app.sh
  • .github/actions/cpflow-detect-release-phase/action.yml
  • .github/actions/cpflow-validate-config/action.yml
  • .github/actions/cpflow-build-docker-image/action.yml

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2829287e64

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

comment_id: commentId,
body: success ? successBody : failureBody
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pin reusable workflow refs to an immutable revision

This change switches deployment automation to a reusable workflow referenced by the mutable branch @jg-codex/reusable-github-actions while also passing secrets: inherit; if that branch is updated, force-pushed, or removed, future runs can execute different code (with access to inherited secrets) or fail entirely, which is a production-impacting reliability and supply-chain regression compared to local, versioned workflow code. Use an immutable commit SHA (or a protected release tag) for the uses: and matching control_plane_flow_ref value.

Useful? React with 👍 / 👎.

@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch from 2829287 to 85b4dc3 Compare May 22, 2026 03:34
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.github/workflows/cpflow-promote-staging-to-production.yml (1)

12-12: ⚡ Quick win

Reassess contents: write for the promotion workflow
The reusable cpflow-promote-staging-to-production.yml flow explicitly “creates a GitHub release after a successful promotion,” so granting permissions: contents: write is consistent with the needs of release creation. Optional: if GitHub supports a narrower scope for the token in this setup (e.g., releases: write), prefer that for least privilege.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/cpflow-promote-staging-to-production.yml at line 12, The
workflow currently grants broad repo content permissions via the permissions key
"contents: write"; update the permissions block in
cpflow-promote-staging-to-production.yml to use the least-privilege scope
required for creating a GitHub release—replace "contents: write" with "releases:
write" if the runner supports that narrower scope, otherwise keep "contents:
write" but add a comment referencing the release creation step to justify the
permission; locate the permissions entry in the workflow and update the value
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/cpflow-promote-staging-to-production.yml:
- Line 12: The workflow currently grants broad repo content permissions via the
permissions key "contents: write"; update the permissions block in
cpflow-promote-staging-to-production.yml to use the least-privilege scope
required for creating a GitHub release—replace "contents: write" with "releases:
write" if the runner supports that narrower scope, otherwise keep "contents:
write" but add a comment referencing the release creation step to justify the
permission; locate the permissions entry in the workflow and update the value
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 911cc27c-f33c-42ce-8f26-0c553fac0ced

📥 Commits

Reviewing files that changed from the base of the PR and between 2829287 and 85b4dc3.

📒 Files selected for processing (14)
  • .github/actions/cpflow-build-docker-image/action.yml
  • .github/actions/cpflow-delete-control-plane-app/action.yml
  • .github/actions/cpflow-delete-control-plane-app/delete-app.sh
  • .github/actions/cpflow-detect-release-phase/action.yml
  • .github/actions/cpflow-setup-environment/action.yml
  • .github/actions/cpflow-validate-config/action.yml
  • .github/actions/cpflow-wait-for-health/action.yml
  • .github/workflows/cpflow-cleanup-stale-review-apps.yml
  • .github/workflows/cpflow-delete-review-app.yml
  • .github/workflows/cpflow-deploy-review-app.yml
  • .github/workflows/cpflow-deploy-staging.yml
  • .github/workflows/cpflow-help-command.yml
  • .github/workflows/cpflow-promote-staging-to-production.yml
  • .github/workflows/cpflow-review-app-help.yml
💤 Files with no reviewable changes (7)
  • .github/actions/cpflow-build-docker-image/action.yml
  • .github/actions/cpflow-delete-control-plane-app/delete-app.sh
  • .github/actions/cpflow-delete-control-plane-app/action.yml
  • .github/actions/cpflow-detect-release-phase/action.yml
  • .github/actions/cpflow-validate-config/action.yml
  • .github/actions/cpflow-setup-environment/action.yml
  • .github/actions/cpflow-wait-for-health/action.yml

@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch 2 times, most recently from 972e90f to f8d6682 Compare May 22, 2026 03:43
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/cpflow-cleanup-stale-review-apps.yml:
- Around line 13-16: The workflow delegates to a mutable branch which, combined
with secrets: inherit, can run arbitrary changed code; update the "uses:"
reference and the "with.control_plane_flow_ref" value to the corresponding
immutable commit SHA for the shakacode/control-plane-flow reusable workflow
(replace jg-codex/reusable-github-actions with the exact commit SHA) so both the
delegated workflow invocation and the control_plane_flow_ref are pinned to the
same SHA; keep secrets: inherit only after pinning and update the other
cpflow-*.yml wrappers the same way to ensure consistent, immutable delegation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 121cd8da-2fbb-48ff-92e6-406bd10c55e0

📥 Commits

Reviewing files that changed from the base of the PR and between 85b4dc3 and 972e90f.

📒 Files selected for processing (14)
  • .github/actions/cpflow-build-docker-image/action.yml
  • .github/actions/cpflow-delete-control-plane-app/action.yml
  • .github/actions/cpflow-delete-control-plane-app/delete-app.sh
  • .github/actions/cpflow-detect-release-phase/action.yml
  • .github/actions/cpflow-setup-environment/action.yml
  • .github/actions/cpflow-validate-config/action.yml
  • .github/actions/cpflow-wait-for-health/action.yml
  • .github/workflows/cpflow-cleanup-stale-review-apps.yml
  • .github/workflows/cpflow-delete-review-app.yml
  • .github/workflows/cpflow-deploy-review-app.yml
  • .github/workflows/cpflow-deploy-staging.yml
  • .github/workflows/cpflow-help-command.yml
  • .github/workflows/cpflow-promote-staging-to-production.yml
  • .github/workflows/cpflow-review-app-help.yml
💤 Files with no reviewable changes (7)
  • .github/actions/cpflow-validate-config/action.yml
  • .github/actions/cpflow-detect-release-phase/action.yml
  • .github/actions/cpflow-build-docker-image/action.yml
  • .github/actions/cpflow-delete-control-plane-app/delete-app.sh
  • .github/actions/cpflow-delete-control-plane-app/action.yml
  • .github/actions/cpflow-wait-for-health/action.yml
  • .github/actions/cpflow-setup-environment/action.yml

Comment thread .github/workflows/cpflow-cleanup-stale-review-apps.yml Outdated
@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch 5 times, most recently from 9a256a9 to b35cb8c Compare May 22, 2026 04:33
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review

Overview

This PR consolidates six local workflow implementations into thin callers that delegate to upstream reusable workflows in shakacode/control-plane-flow. The net result is a large reduction in locally-maintained YAML (~1,800 lines deleted, 30 added), with all the business logic now living in the upstream repo.

The direction is sound — maintaining identical workflow logic in every downstream repo is a maintenance burden. However, there are several concerns worth addressing before merging.


🔴 Critical: Mutable branch ref + secrets: inherit

Every workflow now pins to @jg-codex/reusable-github-actions — a branch name, not an immutable SHA or version tag. Combined with secrets: inherit, this means:

  • A force-push to that upstream branch silently changes behavior in this repo's production and staging pipelines
  • All repository secrets (CPLN_TOKEN_STAGING, CPLN_TOKEN_PRODUCTION, DOCKER_BUILD_SSH_KEY, GITHUB_TOKEN) are inherited by whatever code is at that branch tip at run time

Recommendation: Pin to a commit SHA or a version tag (e.g., @v1.2.3) before merging. This is especially important given the PR description notes this is rolling out features from upstream PRs #303 and #304 — once those are stable, a proper tag or SHA should be used.

# Instead of:
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions

# Prefer:
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@<commit-sha>
# or after a release:
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@v2.3.0

🟠 Security: contents: write permission broadened in promote workflow

cpflow-promote-staging-to-production.yml changed the top-level permissions from contents: read to contents: write. Previously only the create-github-release job scoped up to contents: write. Now the promote-to-production job also runs with write access to repo contents — broader than necessary.

If the upstream reusable workflow needs contents: write for the GitHub release step, that permission should remain scoped to the job that creates the release, not granted workflow-wide.


🟡 Stability: Concurrency controls are no longer visible

The removed local implementations had explicit concurrency: blocks with cancel-in-progress: false and clear rationale (e.g., "a cancelled cpflow deploy-image mid-rollout can leave the review app in a partially-deployed state"). Whether the upstream reusable workflows reproduce this behavior is opaque from this repo's perspective.

If the upstream workflows don't implement equivalent concurrency controls, concurrent pushes during a deploy or a race between delete and cleanup could leave Control Plane apps in partial state.


🟡 Inconsistent control_plane_flow_ref input

cpflow-help-command.yml and cpflow-review-app-help.yml do not pass control_plane_flow_ref, while all other callers explicitly pass control_plane_flow_ref: jg-codex/reusable-github-actions. If the upstream workflows use this input to pin nested action references, the help workflows may silently use a different (possibly main branch) ref.


🟢 Positives

  • The workflow_dispatch gating documentation in .github/cpflow-help.md is clearer — the old text was ambiguous about what "partial smoke test" meant
  • The new REVIEW_APP_DEPLOYING_ICON_URL variable documentation is clean and well-placed in the table
  • The PR description is thorough about what was intentionally left unchanged and why (the cpflow-setup-environment rationale re: upstream PR Broken link in readme file #297)

@@ -8,49 +8,9 @@ on:
permissions:
contents: read
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Mutable ref + secrets: inherit: @jg-codex/reusable-github-actions is a branch name. Any force-push to that branch changes what runs here at next execution — with all repo secrets inherited. Pin to an immutable SHA or version tag before using in production:

Suggested change
contents: read
uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@<COMMIT-SHA-OR-TAG>

issue_number: context.issue.number,
body
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing control_plane_flow_ref input: Same issue as cpflow-help-command.yml — this caller doesn't pass control_plane_flow_ref while every other caller does. Should be consistent:

Suggested change
uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@jg-codex/reusable-github-actions
uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@jg-codex/reusable-github-actions
with:
control_plane_flow_ref: jg-codex/reusable-github-actions

# Don't cancel an in-flight promotion: a half-finished `cpflow deploy-image` plus a
# rollback can leave production in a worse state than letting the first run finish.
cancel-in-progress: false
contents: write
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overly-broad permission: contents: write is now granted at the workflow level. Previously only the create-github-release job scoped up to contents: write; the promote-to-production job ran with contents: read. Granting write access to all jobs in the workflow is broader than necessary — if the upstream reusable workflow needs it for the release step, that permission should be scoped within the upstream workflow's job, not granted here blanket-wide.

Suggested change
contents: write
contents: read

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Help wrappers omit flow ref
    • Added the same control_plane_flow_ref input to both help reusable-workflow callers so they use the intended upstream ref.

Create PR

Or push these changes by commenting:

@cursor push 55b6f9ae42
Preview (55b6f9ae42)
diff --git a/.github/actions/cpflow-build-docker-image/action.yml b/.github/actions/cpflow-build-docker-image/action.yml
deleted file mode 100644
--- a/.github/actions/cpflow-build-docker-image/action.yml
+++ /dev/null
@@ -1,131 +1,0 @@
-name: Build Docker Image
-description: Builds and pushes the app image for a Control Plane workload
-
-inputs:
-  app_name:
-    description: Name of the application
-    required: true
-  org:
-    description: Control Plane organization name
-    required: true
-  commit:
-    description: Commit SHA to tag the image with
-    required: true
-  pr_number:
-    description: Pull request number for status messaging
-    required: false
-  docker_build_extra_args:
-    description: Optional newline-delimited extra docker build tokens. Use key=value forms like --build-arg=FOO=bar.
-    required: false
-  docker_build_ssh_key:
-    description: Optional private SSH key used for Docker builds that fetch private dependencies with RUN --mount=type=ssh
-    required: false
-  docker_build_ssh_known_hosts:
-    description: Optional SSH known_hosts entries used with docker_build_ssh_key. Defaults to pinned GitHub.com host keys.
-    required: false
-  working_directory:
-    description: Directory containing the app .controlplane config and Docker build context
-    required: false
-    default: "."
-
-runs:
-  using: composite
-  steps:
-    # Keep SSH key handling in a dedicated step so DOCKER_BUILD_SSH_KEY is never present
-    # in the main build step's environment. ACTIONS_STEP_DEBUG=true dumps env before any
-    # command runs, so keeping the key out of env there avoids even admin-triggered exposure.
-    - name: Prepare SSH agent for Docker build
-      if: ${{ inputs.docker_build_ssh_key != '' }}
-      shell: bash
-      env:
-        # Pass the key via env so the file write is a single printf call rather than a
-        # heredoc with a fixed terminator (a heredoc would silently truncate the key if
-        # any line of the key value happened to match the terminator). Scope is still
-        # this step only — the build step below does not receive DOCKER_BUILD_SSH_KEY.
-        DOCKER_BUILD_SSH_KEY: ${{ inputs.docker_build_ssh_key }}
-        DOCKER_BUILD_SSH_KNOWN_HOSTS: ${{ inputs.docker_build_ssh_known_hosts }}
-      run: |
-        set -euo pipefail
-
-        umask 077
-        mkdir -p ~/.ssh
-        chmod 700 ~/.ssh
-
-        if [[ -n "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" ]]; then
-          printf '%s\n' "${DOCKER_BUILD_SSH_KNOWN_HOSTS}" > ~/.ssh/known_hosts
-        else
-          printf '%s\n' \
-            'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl' \
-            'github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=' \
-            'github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=' \
-            > ~/.ssh/known_hosts
-        fi
-        chmod 600 ~/.ssh/known_hosts
-
-        printf '%s\n' "${DOCKER_BUILD_SSH_KEY}" > ~/.ssh/cpflow_build_key
-        chmod 600 ~/.ssh/cpflow_build_key
-
-    - name: Build Docker image
-      shell: bash
-      env:
-        APP_NAME: ${{ inputs.app_name }}
-        COMMIT_SHA: ${{ inputs.commit }}
-        CONTROL_PLANE_ORG: ${{ inputs.org }}
-        DOCKER_BUILD_EXTRA_ARGS: ${{ inputs.docker_build_extra_args }}
-        PR_NUMBER: ${{ inputs.pr_number }}
-        WORKING_DIRECTORY: ${{ inputs.working_directory }}
-      run: |
-        set -euo pipefail
-
-        PR_INFO=""
-        docker_build_args=()
-        ssh_agent_started=false
-        build_ssh_prepped=false
-
-        cleanup_build_ssh() {
-          if [[ "${ssh_agent_started}" == "true" ]]; then
-            ssh-agent -k >/dev/null || true
-          fi
-          rm -f "${HOME}/.ssh/cpflow_build_key"
-          # Only remove known_hosts if this action's prep step wrote it. On self-hosted
-          # or reused runners we must not touch a user-managed file we did not create,
-          # so the flag is set inside the same prep-detection branch below.
-          if [[ "${build_ssh_prepped}" == "true" ]]; then
-            rm -f "${HOME}/.ssh/known_hosts"
-          fi
-        }
-        trap cleanup_build_ssh EXIT
-        cd "${WORKING_DIRECTORY}"
-
-        if [[ -n "${PR_NUMBER}" ]]; then
-          PR_INFO=" for PR #${PR_NUMBER}"
-        fi
-
-        if [[ -n "${DOCKER_BUILD_EXTRA_ARGS}" ]]; then
-          while IFS= read -r arg; do
-            arg="${arg%$'\r'}"
-            [[ -n "${arg}" ]] || continue
-
-            if [[ "${arg}" =~ [[:space:]] ]]; then
-              echo "docker_build_extra_args entries must be single docker-build tokens. " \
-                   "Use key=value forms like --build-arg=FOO=bar." >&2
-              exit 1
-            fi
-
-            docker_build_args+=("${arg}")
-          done <<< "${DOCKER_BUILD_EXTRA_ARGS}"
-        fi
-
-        if [[ -f "${HOME}/.ssh/cpflow_build_key" ]]; then
-          # Mark prep-step ownership so cleanup_build_ssh only removes known_hosts
-          # when this action wrote it (see trap above).
-          build_ssh_prepped=true
-          eval "$(ssh-agent -s)"
-          ssh_agent_started=true
-          ssh-add "${HOME}/.ssh/cpflow_build_key"
-          docker_build_args+=("--ssh=default")
-        fi
-
-        echo "🏗️ Building Docker image${PR_INFO} (commit ${COMMIT_SHA})..."
-        cpflow build-image -a "${APP_NAME}" --commit="${COMMIT_SHA}" --org="${CONTROL_PLANE_ORG}" "${docker_build_args[@]}"
-        echo "✅ Docker image build successful${PR_INFO} (commit ${COMMIT_SHA})"
\ No newline at end of file

diff --git a/.github/actions/cpflow-delete-control-plane-app/action.yml b/.github/actions/cpflow-delete-control-plane-app/action.yml
deleted file mode 100644
--- a/.github/actions/cpflow-delete-control-plane-app/action.yml
+++ /dev/null
@@ -1,24 +1,0 @@
-name: Delete Control Plane App
-description: Deletes a Control Plane app and all associated resources
-
-inputs:
-  app_name:
-    description: Name of the application to delete
-    required: true
-  cpln_org:
-    description: Control Plane organization name
-    required: true
-  review_app_prefix:
-    description: Prefix used for review app names
-    required: true
-
-runs:
-  using: composite
-  steps:
-    - name: Delete application
-      shell: bash
-      run: ${{ github.action_path }}/delete-app.sh
-      env:
-        APP_NAME: ${{ inputs.app_name }}
-        CPLN_ORG: ${{ inputs.cpln_org }}
-        REVIEW_APP_PREFIX: ${{ inputs.review_app_prefix }}
\ No newline at end of file

diff --git a/.github/actions/cpflow-delete-control-plane-app/delete-app.sh b/.github/actions/cpflow-delete-control-plane-app/delete-app.sh
deleted file mode 100644
--- a/.github/actions/cpflow-delete-control-plane-app/delete-app.sh
+++ /dev/null
@@ -1,50 +1,0 @@
-#!/bin/bash
-
-set -euo pipefail
-
-: "${APP_NAME:?APP_NAME environment variable is required}"
-: "${CPLN_ORG:?CPLN_ORG environment variable is required}"
-: "${REVIEW_APP_PREFIX:?REVIEW_APP_PREFIX environment variable is required}"
-
-expected_prefix="${REVIEW_APP_PREFIX}-"
-if [[ "$APP_NAME" != "${expected_prefix}"* ]]; then
-  echo "❌ ERROR: refusing to delete an app outside the review app prefix" >&2
-  echo "App name: $APP_NAME" >&2
-  echo "Expected prefix: ${expected_prefix}" >&2
-  exit 1
-fi
-
-echo "🔍 Checking if application exists: $APP_NAME"
-exists_output=""
-set +e
-exists_output="$(cpflow exists -a "$APP_NAME" --org "$CPLN_ORG" 2>&1)"
-exists_status=$?
-set -e
-
-case "$exists_status" in
-  0)
-    ;;
-  3)
-    if [[ -n "$exists_output" ]]; then
-      printf '%s\n' "$exists_output"
-    fi
-    echo "⚠️ Application does not exist: $APP_NAME"
-    exit 0
-    ;;
-  *)
-    echo "❌ ERROR: failed to determine whether application exists: $APP_NAME" >&2
-    if [[ -n "$exists_output" ]]; then
-      printf '%s\n' "$exists_output" >&2
-    fi
-    exit "$exists_status"
-    ;;
-esac
-
-if [[ -n "$exists_output" ]]; then
-  printf '%s\n' "$exists_output"
-fi
-
-echo "🗑️ Deleting application: $APP_NAME"
-cpflow delete -a "$APP_NAME" --org "$CPLN_ORG" --yes
-
-echo "✅ Successfully deleted application: $APP_NAME"
\ No newline at end of file

diff --git a/.github/actions/cpflow-detect-release-phase/action.yml b/.github/actions/cpflow-detect-release-phase/action.yml
deleted file mode 100644
--- a/.github/actions/cpflow-detect-release-phase/action.yml
+++ /dev/null
@@ -1,62 +1,0 @@
-name: Detect release phase support
-description: >-
-  Inspects .controlplane/controlplane.yml for an app and emits `flag=--run-release-phase`
-  when a `release_script:` is configured. Outputs an empty `flag` otherwise.
-
-inputs:
-  app_name:
-    description: cpflow app name to inspect
-    required: true
-  working_directory:
-    description: Directory containing .controlplane/controlplane.yml
-    required: false
-    default: "."
-
-outputs:
-  flag:
-    description: Either `--run-release-phase` or empty
-    value: ${{ steps.detect.outputs.flag }}
-
-runs:
-  using: composite
-  steps:
-    - name: Detect release phase support
-      id: detect
-      shell: bash
-      env:
-        APP_NAME: ${{ inputs.app_name }}
-        WORKING_DIRECTORY: ${{ inputs.working_directory }}
-      run: |
-        set -euo pipefail
-        cd "${WORKING_DIRECTORY}"
-
-        release_script="$(ruby - "${APP_NAME}" <<'RUBY'
-        require "yaml"
-
-        app_name = ARGV.fetch(0)
-        data = YAML.safe_load(File.read(".controlplane/controlplane.yml"), aliases: true)
-        apps = data["apps"] || {}
-        app_config = apps[app_name]
-
-        unless app_config
-          app_config = apps.find do |name, config|
-            config.is_a?(Hash) &&
-              config["match_if_app_name_starts_with"] &&
-              app_name.start_with?(name)
-          end&.last
-        end
-
-        unless app_config.is_a?(Hash)
-          warn "Error: app '#{app_name}' is not defined under `apps:` in `.controlplane/controlplane.yml`."
-          exit 1
-        end
-
-        puts app_config["release_script"].to_s
-        RUBY
-        )"
-
-        if [[ -n "${release_script}" ]]; then
-          echo "flag=--run-release-phase" >> "$GITHUB_OUTPUT"
-        else
-          echo "flag=" >> "$GITHUB_OUTPUT"
-        fi
\ No newline at end of file

diff --git a/.github/actions/cpflow-setup-environment/action.yml b/.github/actions/cpflow-setup-environment/action.yml
deleted file mode 100644
--- a/.github/actions/cpflow-setup-environment/action.yml
+++ /dev/null
@@ -1,110 +1,0 @@
-name: Setup Control Plane Environment
-description: Sets up Ruby, installs the Control Plane CLI and cpflow gem, and configures a default profile
-
-inputs:
-  token:
-    description: Control Plane token
-    required: true
-  org:
-    description: Control Plane organization
-    required: true
-  ruby_version:
-    description: >-
-      Ruby version used for cpflow. When empty (the default), ruby/setup-ruby auto-detects
-      from .ruby-version, .tool-versions, or the Gemfile.
-    required: false
-    default: ""
-  # GitHub parses double-brace expression snippets inside action metadata (including
-  # `description:`) while loading the composite action, and the `vars` context is not
-  # available in that phase. Keep these descriptions in plain prose - reference repo
-  # variables by NAME only, never with literal GitHub Actions expression syntax.
-  cpln_cli_version:
-    description: >-
-      @controlplane/cli version. Empty string falls back to the action's pinned default,
-      so callers can wire this input to the CPLN_CLI_VERSION repository variable
-      unconditionally.
-    required: false
-    default: ""
-  cpflow_version:
-    description: >-
-      cpflow gem version. Empty string falls back to the action's pinned default,
-      so callers can wire this input to the CPFLOW_VERSION repository variable
-      unconditionally.
-    required: false
-    default: ""
-
-runs:
-  using: composite
-  # Third-party actions are pinned to floating major tags (`@v4`, `@v1`, `@v7`) rather than
-  # immutable SHAs. SHA pinning is GitHub's stronger security recommendation, but for
-  # generated templates that ship into many downstream repositories floating tags are
-  # easier for users to keep current and Dependabot/Renovate already cover the SHA-pinning
-  # workflow for repositories that opt in. Repositories with stricter supply-chain
-  # requirements should replace each `uses: actions/...@vN` with the corresponding
-  # immutable commit SHA.
-  steps:
-    - name: Set up Ruby
-      uses: ruby/setup-ruby@v1
-      with:
-        ruby-version: ${{ inputs.ruby_version }}
-
-    - name: Install Control Plane CLI and cpflow gem
-      shell: bash
-      env:
-        CPLN_CLI_VERSION: ${{ inputs.cpln_cli_version }}
-        CPFLOW_VERSION: ${{ inputs.cpflow_version }}
-      run: |
-        set -euo pipefail
-
-        # Bump these defaults when a new release lands that you want to roll out by default.
-        # Override per-repo by setting `CPLN_CLI_VERSION` / `CPFLOW_VERSION` repo variables;
-        # an empty input falls back to the action's pinned default below.
-        default_cpln_cli_version="3.3.1"
-        default_cpflow_version="5.0.0.rc.1"
-
-        CPLN_CLI_VERSION="${CPLN_CLI_VERSION:-${default_cpln_cli_version}}"
-        CPFLOW_VERSION="${CPFLOW_VERSION:-${default_cpflow_version}}"
-
-        npm_global_prefix="${HOME}/.npm-global"
-        mkdir -p "${npm_global_prefix}"
-        echo "${npm_global_prefix}/bin" >> "$GITHUB_PATH"
-        export PATH="${npm_global_prefix}/bin:${PATH}"
-
-        npm install --global --prefix "${npm_global_prefix}" "@controlplane/cli@${CPLN_CLI_VERSION}"
-        cpln --version
-
-        gem install cpflow -v "${CPFLOW_VERSION}" --no-document
-        cpflow --version
-
-    - name: Setup Control Plane profile and registry login
-      shell: bash
-      env:
-        # Pass the token via CPLN_TOKEN so cpln picks it up from the environment
-        # rather than `--token`, which would leak it into /proc/<pid>/cmdline and ps output.
-        CPLN_TOKEN: ${{ inputs.token }}
-        ORG: ${{ inputs.org }}
-      run: |
-        set -euo pipefail
-
-        if [[ -z "$CPLN_TOKEN" ]]; then
-          echo "Error: Control Plane token not provided" >&2
-          exit 1
-        fi
-
-        if [[ -z "$ORG" ]]; then
-          echo "Error: Control Plane organization not provided" >&2
-          exit 1
-        fi
-
-        # Keep the service-account token available to later cpflow/cpln steps.
-        # The profile stores org/default metadata, but cpflow direct API calls
-        # read CPLN_TOKEN before falling back to `cpln profile token`.
-        echo "::add-mask::${CPLN_TOKEN}"
-        printf 'CPLN_TOKEN=%s\n' "${CPLN_TOKEN}" >> "${GITHUB_ENV}"
-
-        # `cpln profile update` lists `create` as an alias (cpln profile --help) and is
-        # idempotent: it creates the profile if missing and updates it otherwise. Calling
-        # update directly avoids parsing the CLI's "already exists" English error text,
-        # which would silently swallow a real failure if the wording ever changed.
-        cpln profile update default --org "$ORG"
-        cpln image docker-login --org "$ORG"
\ No newline at end of file

diff --git a/.github/actions/cpflow-validate-config/action.yml b/.github/actions/cpflow-validate-config/action.yml
deleted file mode 100644
--- a/.github/actions/cpflow-validate-config/action.yml
+++ /dev/null
@@ -1,85 +1,0 @@
-name: Validate cpflow GitHub configuration
-description: >-
-  Validates that required secrets and repository variables are set before a workflow
-  proceeds. Pass each value via `env:` with the same NAME as the secret or variable,
-  then list the required entries in `required` as `type:NAME` pairs (type is `secret`
-  or `variable`). When `pull_request_friendly: true` and the current event is a
-  pull request event, missing config writes a step summary and exits 0 with
-  `ready=false` instead of failing the job.
-
-inputs:
-  required:
-    description: |
-      Newline-separated `type:NAME` pairs. Type is `secret` or `variable`. The
-      caller MUST export the matching values via `env:` using the same NAME.
-    required: true
-  pull_request_friendly:
-    description: When "true" and event is pull_request/pull_request_target, write summary and exit 0 with ready=false.
-    required: false
-    default: "false"
-
-outputs:
-  ready:
-    description: '"true" when all values are set, "false" when missing in PR-friendly mode.'
-    value: ${{ steps.check.outputs.ready }}
-
-runs:
-  using: composite
-  steps:
-    - name: Check required secrets and variables
-      id: check
-      shell: bash
-      env:
-        CPFLOW_REQUIRED: ${{ inputs.required }}
-        CPFLOW_PR_FRIENDLY: ${{ inputs.pull_request_friendly }}
-        CPFLOW_EVENT_NAME: ${{ github.event_name }}
-      run: |
-        set -euo pipefail
-
-        missing=()
-        while IFS= read -r entry; do
-          entry="${entry%$'\r'}"
-          entry="${entry## }"
-          entry="${entry%% }"
-          [[ -z "${entry}" ]] && continue
-
-          type="${entry%%:*}"
-          name="${entry#*:}"
-
-          # Reject names that are not plain SHELL_VAR identifiers before doing the
-          # indirect lookup below. Without this guard, ${!name} would expand whatever
-          # bash nameref/transformation a hand-edited generated workflow snuck in
-          # (e.g. `BASH_FUNC_foo%%`). Callers today are the generated templates, but
-          # the generated file lives in the user's repo and can be hand-edited.
-          if [[ ! "${name}" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then
-            echo "Invalid config entry name: ${name}" >&2
-            exit 1
-          fi
-
-          # Indirect bash lookup: reads the env var named by ${name} (e.g. CPLN_TOKEN_STAGING)
-          # so the value never has to round-trip through workflow logs.
-          if [[ -z "${!name:-}" ]]; then
-            missing+=("${type}:${name}")
-          fi
-        done <<< "${CPFLOW_REQUIRED}"
-
-        if [[ ${#missing[@]} -eq 0 ]]; then
-          echo "ready=true" >> "$GITHUB_OUTPUT"
-          exit 0
-        fi
-
-        if [[ "${CPFLOW_PR_FRIENDLY}" == "true" && ( "${CPFLOW_EVENT_NAME}" == "pull_request" || "${CPFLOW_EVENT_NAME}" == "pull_request_target" ) ]]; then
-          echo "ready=false" >> "$GITHUB_OUTPUT"
-          {
-            echo "Control Plane review app automation is not configured yet."
-            echo
-            echo "Missing required GitHub configuration:"
-            printf -- '- `%s`\n' "${missing[@]}"
-            echo
-            echo "Pushes to this pull request will skip review app deploys until the repository is configured."
-          } >> "$GITHUB_STEP_SUMMARY"
-          exit 0
-        fi
-
-        printf 'Missing required GitHub configuration:\n- %s\n' "${missing[@]}" >&2
-        exit 1
\ No newline at end of file

diff --git a/.github/actions/cpflow-wait-for-health/action.yml b/.github/actions/cpflow-wait-for-health/action.yml
deleted file mode 100644
--- a/.github/actions/cpflow-wait-for-health/action.yml
+++ /dev/null
@@ -1,92 +1,0 @@
-name: Wait for Control Plane workload health
-description: >-
-  Polls the workload's status endpoint with curl and exits success when the
-  HTTP response status is in the accepted list. Fails non-zero (and reports
-  `healthy=false`) once retries are exhausted.
-
-inputs:
-  workload_name:
-    description: Workload to query (e.g. `rails`).
-    required: true
-  app_name:
-    description: GVC / Control Plane app name the workload belongs to.
-    required: true
-  org:
-    description: Control Plane organization.
-    required: true
-  max_retries:
-    description: Number of attempts before giving up.
-    required: false
-    default: "24"
-  interval_seconds:
-    description: Seconds to sleep between attempts.
-    required: false
-    default: "15"
-  accepted_statuses:
-    description: >-
-      Space-separated list of HTTP status codes considered healthy. The default
-      `200 301 302` accepts redirects because curl is invoked without `-L`, so a
-      root path that auth-redirects looks like a redirect, not a failure.
-    required: false
-    default: "200 301 302"
-  curl_max_time:
-    description: Per-request curl timeout, seconds.
-    required: false
-    default: "10"
-
-outputs:
-  healthy:
-    description: '"true" once a healthy response was observed; "false" otherwise.'
-    value: ${{ steps.poll.outputs.healthy }}
-
-runs:
-  using: composite
-  steps:
-    - name: Poll workload endpoint
-      id: poll
-      shell: bash
-      env:
-        CPFLOW_WORKLOAD_NAME: ${{ inputs.workload_name }}
-        CPFLOW_APP_NAME: ${{ inputs.app_name }}
-        CPFLOW_ORG: ${{ inputs.org }}
-        CPFLOW_MAX_RETRIES: ${{ inputs.max_retries }}
-        CPFLOW_INTERVAL_SECONDS: ${{ inputs.interval_seconds }}
-        CPFLOW_ACCEPTED_STATUSES: ${{ inputs.accepted_statuses }}
-        CPFLOW_CURL_MAX_TIME: ${{ inputs.curl_max_time }}
-      run: |
-        set -euo pipefail
-
-        read -r -a accepted_statuses <<< "${CPFLOW_ACCEPTED_STATUSES}"
-
-        for attempt in $(seq 1 "${CPFLOW_MAX_RETRIES}"); do
-          echo "Health check attempt ${attempt}/${CPFLOW_MAX_RETRIES}"
-
-          if ! workload_json="$(cpln workload get "${CPFLOW_WORKLOAD_NAME}" --gvc "${CPFLOW_APP_NAME}" --org "${CPFLOW_ORG}" -o json 2>&1)"; then
-            echo "::error::Workload '${CPFLOW_WORKLOAD_NAME}' not found in GVC '${CPFLOW_APP_NAME}'. Set PRIMARY_WORKLOAD to the correct workload name." >&2
-            printf '%s\n' "${workload_json}" >&2
-            echo "healthy=false" >> "$GITHUB_OUTPUT"
-            exit 1
-          fi
-
-          endpoint="$(echo "${workload_json}" | jq -r '.status.endpoint // empty')"
-          if [[ -n "${endpoint}" ]]; then
-            http_status="$(curl -s -o /dev/null -w '%{http_code}' --max-time "${CPFLOW_CURL_MAX_TIME}" "${endpoint}" 2>/dev/null || echo 000)"
-            echo "Endpoint: ${endpoint}, HTTP status: ${http_status}"
-
-            for accepted in "${accepted_statuses[@]}"; do
-              if [[ "${http_status}" == "${accepted}" ]]; then
-                echo "healthy=true" >> "$GITHUB_OUTPUT"
-                exit 0
-              fi
-            done
-          else
-            echo "Workload '${CPFLOW_WORKLOAD_NAME}' has no endpoint yet; waiting for one to be assigned."
-          fi
-
-          if [[ "${attempt}" -lt "${CPFLOW_MAX_RETRIES}" ]]; then
-            sleep "${CPFLOW_INTERVAL_SECONDS}"
-          fi
-        done
-
-        echo "healthy=false" >> "$GITHUB_OUTPUT"
-        exit 1
\ No newline at end of file

diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml
--- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml
+++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml
@@ -8,49 +8,9 @@
 permissions:
   contents: read
 
-concurrency:
-  # Single global group: only one cleanup sweep at a time. Independent of review-app
-  # deploy/delete groups (different keys), so cleanup will not block per-PR work.
-  group: cpflow-cleanup-stale-review-apps
-  # A cancelled `cpflow cleanup-stale-apps` can leave half-deleted review apps; let
-  # the in-flight run finish before the next scheduled tick begins.
-  cancel-in-progress: false
-
 jobs:
   cleanup:
-    runs-on: ubuntu-latest
-    timeout-minutes: 30
-    steps:
-      - name: Checkout repository
-        uses: actions/checkout@v6
-        with:
-          persist-credentials: false
-
-      - name: Validate required secrets and variables
-        uses: ./.github/actions/cpflow-validate-config
-        env:
-          CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
-          CPLN_ORG_STAGING: ${{ vars.CPLN_ORG_STAGING }}
-          REVIEW_APP_PREFIX: ${{ vars.REVIEW_APP_PREFIX }}
-        with:
-          required: |
-            secret:CPLN_TOKEN_STAGING
-            variable:CPLN_ORG_STAGING
-            variable:REVIEW_APP_PREFIX
-
-      - name: Setup environment
-        uses: ./.github/actions/cpflow-setup-environment
-        with:
-          token: ${{ secrets.CPLN_TOKEN_STAGING }}
-          org: ${{ vars.CPLN_ORG_STAGING }}
-          cpln_cli_version: ${{ vars.CPLN_CLI_VERSION }}
-          cpflow_version: ${{ vars.CPFLOW_VERSION }}
-
-      - name: Remove stale review apps
-        env:
-          REVIEW_APP_PREFIX: ${{ vars.REVIEW_APP_PREFIX }}
-          CPLN_ORG_STAGING: ${{ vars.CPLN_ORG_STAGING }}
-        shell: bash
-        run: |
-          set -euo pipefail
-          cpflow cleanup-stale-apps -a "${REVIEW_APP_PREFIX}" --org "${CPLN_ORG_STAGING}" --yes
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@jg-codex/reusable-github-actions
+    with:
+      control_plane_flow_ref: jg-codex/reusable-github-actions
+    secrets: inherit

diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml
--- a/.github/workflows/cpflow-delete-review-app.yml
+++ b/.github/workflows/cpflow-delete-review-app.yml
@@ -17,17 +17,6 @@
   issues: write
   pull-requests: write
 
-concurrency:
-  group: cpflow-delete-review-app-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
-  # Deletions must not cancel each other mid-flight — a cancelled `cpln` delete can leave
-  # partial state behind. Let the in-progress deletion finish before the next run starts.
-  cancel-in-progress: false
-
-env:
-  APP_NAME: ${{ vars.REVIEW_APP_PREFIX }}-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
-  CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }}
-  PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
-
 jobs:
   delete-review-app:
     if: |
@@ -37,147 +26,7 @@
        contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
       (github.event_name == 'pull_request_target' && github.event.action == 'closed') ||
       github.event_name == 'workflow_dispatch'
-    runs-on: ubuntu-latest
-    timeout-minutes: 15
-
-    steps:
-      - name: React to delete command
-        if: github.event_name == 'issue_comment'
-        continue-on-error: true
-        uses: actions/github-script@v8
-        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
-      # out the base branch's trusted workflow code, not the fork head. Do not add
-      # `ref: ${{ github.event.pull_request.head.sha }}` here without re-evaluating
-      # 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@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
-          # `pull_request_target`, which has access to repository secrets.
-          persist-credentials: false
-
-      - name: Validate required secrets and variables
-        id: config
-        uses: ./.github/actions/cpflow-validate-config
-        env:
-          CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
-          CPLN_ORG_STAGING: ${{ vars.CPLN_ORG_STAGING }}
-          REVIEW_APP_PREFIX: ${{ vars.REVIEW_APP_PREFIX }}
-        with:
-          required: |
-            secret:CPLN_TOKEN_STAGING
-            variable:CPLN_ORG_STAGING
-            variable:REVIEW_APP_PREFIX
-          pull_request_friendly: "true"
-
-      - name: Setup environment
-        if: steps.config.outputs.ready == 'true'
-        uses: ./.github/actions/cpflow-setup-environment
-        with:
-          token: ${{ secrets.CPLN_TOKEN_STAGING }}
-          org: ${{ vars.CPLN_ORG_STAGING }}
-          cpln_cli_version: ${{ vars.CPLN_CLI_VERSION }}
-          cpflow_version: ${{ vars.CPFLOW_VERSION }}
-
-      - name: Set workflow links
-        if: steps.config.outputs.ready == 'true'
-        uses: actions/github-script@v8
-        with:
-          script: |
-            const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
-            core.exportVariable("WORKFLOW_URL", workflowUrl);
-            core.exportVariable(
-              "CONSOLE_URL",
-              `https://console.cpln.io/console/org/${process.env.CPLN_ORG}/-info`
-            );
-
-      - name: Create initial PR comment
-        if: steps.config.outputs.ready == 'true'
-        id: create-comment
-        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
-            });
-            core.setOutput("comment-id", comment.data.id);
-
-      - name: Delete review app
-        if: steps.config.outputs.ready == 'true'
-        uses: ./.github/actions/cpflow-delete-control-plane-app
-        with:
-          app_name: ${{ env.APP_NAME }}
-          cpln_org: ${{ vars.CPLN_ORG_STAGING }}
-          review_app_prefix: ${{ vars.REVIEW_APP_PREFIX }}
-
-      # Finalizer still runs after delete failures, but only after config validation
-      # 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@v8
-        env:
-          COMMENT_ID: ${{ steps.create-comment.outputs.comment-id }}
-          JOB_STATUS: ${{ job.status }}
-        with:
-          script: |
-            const commentId = Number(process.env.COMMENT_ID);
... diff truncated: showing 800 of 2092 lines

You can send follow-ups to the cloud agent here.

issue_number: prNumber,
body: helpText
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Help wrappers omit flow ref

Medium Severity

The help reusable-workflow callers invoke shakacode/control-plane-flow at jg-codex/reusable-github-actions but, unlike every other regenerated cpflow-* wrapper, they do not pass control_plane_flow_ref. If the upstream help workflows use that input to check out shared actions or scripts, help runs can default to a different ref than deploy/delete and miss the intended Node 24 or reaction behavior.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b35cb8c. Configure here.

issue_number: prNumber,
body: helpText
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing control_plane_flow_ref input: Every other caller explicitly passes control_plane_flow_ref: jg-codex/reusable-github-actions, but this workflow omits it. If the upstream workflow uses that input to resolve nested action refs, this caller may silently use a different (possibly main) branch. Should be consistent with the other callers:

Suggested change
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
with:
control_plane_flow_ref: jg-codex/reusable-github-actions

@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch from b35cb8c to 3462b56 Compare May 22, 2026 04:36
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review: Regenerate cpflow review app feedback

Overview

This PR migrates all cpflow-* GitHub Actions workflows from in-repo local implementations (~1,800 lines of well-tested workflow/action YAML) to thin callers of upstream reusable workflows in shakacode/control-plane-flow. The reduction in local maintenance surface is appealing, but the current diff introduces several concerns that should be resolved before merging.


🔴 Critical: Pinning to a development branch

Every reusable-workflow call pins to @jg-codex/reusable-github-actions, which is a feature branch in an external repo, not a release tag or immutable SHA:

uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions

This is the single biggest concern:

  • Any force-push to that branch silently changes the behaviour of all deploy, delete, cleanup, promote, and help workflows in this repo.
  • GitHub's supply-chain guidance recommends pinning to an immutable commit SHA (or at minimum a semver tag) for external workflow refs.
  • Combined with secrets: inherit (see below), a compromised or accidentally modified branch could exfiltrate CPLN_TOKEN_PRODUCTION and every other secret.

Before merging, this should be changed to a pinned release tag (e.g. @v1.2.3) or an immutable SHA once the upstream PRs (#303, #304) are merged to a stable ref.


🔴 Critical: secrets: inherit with an unpinned external ref

secrets: inherit passes every repository secret — including CPLN_TOKEN_PRODUCTION, CPLN_TOKEN_STAGING, DOCKER_BUILD_SSH_KEY, and GITHUB_TOKEN — to the upstream reusable workflow. This is appropriate once the ref is stable and audited, but paired with an unpinned feature branch it means any change upstream immediately has access to production credentials.


🟡 Medium: contents: write permission escalation in promote workflow

cpflow-promote-staging-to-production.yml changed the top-level permission from contents: readcontents: write. The previous code required contents: write only in the separate create-github-release job (which had its own scoped permissions: block). Now that job's logic has moved upstream, the caller grants write access to the whole workflow with no corresponding local step that needs it. If the upstream reusable workflow creates releases internally, the permission should be documented; if not, this should stay contents: read.


🟡 Medium: Concurrency guards removed without upstream confirmation

The caller workflows no longer declare concurrency: groups. The removed guards prevented:

  • Concurrent cpflow cleanup-stale-apps runs leaving half-deleted apps
  • Concurrent per-PR deploy/delete runs leaving workloads in partially-rolled-out state
  • Concurrent production promotions

Whether the upstream reusable workflows replicate these guards is not visible from this diff. If they do not, there is a real risk of race conditions on staging and production. Please confirm (and ideally link to the upstream workflow source) that equivalent concurrency: cancel-in-progress: false groups exist upstream.


🟡 Medium: cpflow-help-command.yml missing with: block

Every other reusable-workflow call passes control_plane_flow_ref: jg-codex/reusable-github-actions via with:, but the help-command caller does not include a with: block at all. If the upstream cpflow-help-command.yml accepts or requires that input it will silently use a default (or fail). Align this with the other callers for consistency and correctness.


🟢 Low: PR description vs. diff discrepancy

The PR description states:

"I intentionally kept the existing downstream cpflow-setup-environment action unchanged"

However, the diff deletes .github/actions/cpflow-setup-environment/action.yml. It appears the intent was to not regenerate it from upstream main, but since the workflows no longer reference local composite actions at all, the file was removed anyway. The description should be updated to avoid confusion for future readers of the git log.


🟢 Low: Documentation improvement is good

The cpflow-help.md changes are clear improvements — the REVIEW_APP_DEPLOYING_ICON_URL row is well-documented, and the revised testing guidance ("supported way to test PR-branch workflow edits before merge") is more accurate and actionable than the previous "partial smoke test" framing.


Summary

# Severity Item
1 🔴 Critical All external refs point to a feature branch, not a tag/SHA
2 🔴 Critical secrets: inherit passes production credentials to an unpinned branch
3 🟡 Medium contents: write escalation in promote workflow may be unneeded
4 🟡 Medium Concurrency guards removed — need upstream confirmation they exist
5 🟡 Medium cpflow-help-command.yml missing with: control_plane_flow_ref:
6 🟢 Low PR description says cpflow-setup-environment was kept; diff deletes it

The overall direction is sound — centralising these workflows upstream reduces duplication. The blocking items are the branch pinning and the secrets: inherit surface area. Recommend merging only after the upstream PRs land on a stable tag/SHA and the ref is updated here accordingly.

@@ -8,49 +8,9 @@ on:
permissions:
contents: read
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Supply chain risk: This ref points to a feature branch (jg-codex/reusable-github-actions), not an immutable tag or SHA. Any force-push to that branch in the upstream repo silently changes the behaviour of this workflow. Combined with secrets: inherit below, a compromised or accidentally modified branch can exfiltrate CPLN_TOKEN_STAGING.

Please pin to a release tag (e.g. @v1.2.3) or a full commit SHA once the upstream PRs (#303, #304) land on a stable ref.

Suggested change
contents: read
uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@<REPLACE_WITH_TAG_OR_SHA>

# Don't cancel an in-flight promotion: a half-finished `cpflow deploy-image` plus a
# rollback can leave production in a worse state than letting the first run finish.
cancel-in-progress: false
contents: write
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Permission escalation: The previous code used contents: read at the top level and scoped contents: write only to the create-github-release job. Now contents: write is granted to the entire workflow at the top level. If the upstream reusable workflow handles release creation internally that's fine, but it should be documented. If not, this should revert to contents: read.

Suggested change
contents: write
contents: read

issue_number: prNumber,
body: helpText
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inconsistency with other callers: Every other reusable-workflow call in this PR includes a with: control_plane_flow_ref: block, but this caller does not. If the upstream cpflow-help-command.yml accepts that input it will silently use a default that may not match jg-codex/reusable-github-actions. Align with the other callers for consistency and to avoid silent default mismatch.

Suggested change
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
with:
control_plane_flow_ref: jg-codex/reusable-github-actions

@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch from 3462b56 to f2246c2 Compare May 22, 2026 04:40
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review

Overview

This PR consolidates cpflow CI by delegating all review-app, staging, and production workflow logic to reusable workflows in the upstream shakacode/control-plane-flow repository. The local composite actions (cpflow-build-docker-image, cpflow-delete-control-plane-app, cpflow-detect-release-phase, cpflow-setup-environment, cpflow-validate-config, cpflow-wait-for-health) are all deleted (~1800 lines removed), and every downstream workflow is reduced to a single uses: call.

The direction is sound — upstream-owned reusable workflows are easier to maintain and keep in sync. However there are a few concerns worth addressing before merge.


Critical: Supply-chain / secret exposure risk

All seven reusable-workflow calls are pinned to a mutable branch ref (@jg-codex/reusable-github-actions) and pass secrets: inherit.

A mutable branch can be force-pushed at any time, silently changing what runs in your CI without any PR or review in this repo. Combined with secrets: inherit, a branch rewrite in the upstream repo would immediately expose CPLN_TOKEN_STAGING, CPLN_TOKEN_PRODUCTION, DOCKER_BUILD_SSH_KEY, and every other secret to the attacker's code.

Recommendation: Pin to an immutable commit SHA (e.g. @abc1234...) or to a signed release tag, not a branch name. The deleted cpflow-setup-environment/action.yml had a comment explaining exactly this trade-off (# Third-party actions are pinned to floating major tags...); the same reasoning applies here, but branches are far weaker guarantees than even floating major tags.


High: permissions: contents: write scope widened in promote workflow

cpflow-promote-staging-to-production.yml previously granted contents: write only to the create-github-release child job (scoped via job-level permissions:). This PR moves to contents: write at the top-level workflow permission block. Because the reusable workflow receives the calling workflow's permission token, every step in the upstream promote workflow now runs with write access to repo contents — not just the release-creation step.


Medium: control_plane_flow_ref: jg-codex/reusable-github-actions passed as an input

The reusable workflows receive the branch name as a with: input. If the upstream workflow uses this ref to check out additional code or call further nested reusable workflows, the mutable-ref risk is compounded. It also means the behaviour of control_plane_flow_ref: is entirely opaque to this repo — a future upstream change to how that input is consumed would silently change CI.


Medium: Concurrency guards removed with no local equivalent

The local workflows had carefully documented concurrency: blocks with cancel-in-progress: false to prevent half-deleted apps and partially-rolled-back deployments. Those are now gone from this repo. Whether the upstream reusable workflows implement equivalent guards is not auditable from here (see the mutable-ref concern above).


Low: Pre-merge ref

The PR description says upstream PRs #303 and #304 have already merged, but the workflows reference jg-codex/reusable-github-actions (a feature branch) rather than main or a release tag. If the intent is to ship merged upstream code, the ref should be updated — or this should be an explicit hold until upstream cuts a stable tag.


Documentation (minor)

The updated help text in .github/cpflow-help.md improves clarity for most cases. The old wording called out that changes under .github/actions/ still need to merge to the default branch before they take effect in comment-triggered runs. With local composite actions deleted, this caveat no longer applies — the updated text is correct as written.

@@ -8,49 +8,9 @@ on:
permissions:
contents: read
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All seven workflow calls use @jg-codex/reusable-github-actions — a mutable branch — combined with secrets: inherit. A force-push to that branch in the upstream repo would silently change what code runs against your secrets (CPLN_TOKEN_STAGING, DOCKER_BUILD_SSH_KEY, etc.) with no PR in this repository.

Pin to an immutable SHA instead:

Suggested change
contents: read
uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@<commit-sha>

Or at minimum pin to a release tag (@v1, @v2.x) so updates require a deliberate bump. The same applies to every other uses: line in this PR.

comment_id: commentId,
body: success ? successBody : failureBody
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Passing the branch name as control_plane_flow_ref multiplies the mutable-ref risk: if the upstream reusable workflow uses this input to check out further code or call nested workflows, the attack surface widens. It also makes the behaviour of this input entirely opaque to this repo — a future upstream change to how control_plane_flow_ref is consumed would silently change CI without any review here.

@@ -14,129 +14,10 @@ on:
permissions:
contents: read

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The concurrency: block that prevented mid-flight deploy cancellations (cancel-in-progress: false) was removed. The old comment explained the risk clearly: a cancelled cpflow deploy-image mid-rollout can leave staging in a partially-deployed state. Confirm the upstream reusable workflow implements equivalent concurrency guards, otherwise a rapid push sequence could trigger this failure mode.

# Don't cancel an in-flight promotion: a half-finished `cpflow deploy-image` plus a
# rollback can leave production in a worse state than letting the first run finish.
cancel-in-progress: false
contents: write
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The top-level permissions: changed from contents: read to contents: write. Previously, contents: write was scoped only to the create-github-release child job. Now the entire workflow — and therefore the upstream reusable workflow — runs with write access to repository contents.

Consider keeping contents: read here and letting the upstream reusable workflow request contents: write only for the job that needs it, or confirm that the upstream workflow re-scopes permissions at the job level.

@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch from f2246c2 to 37222b2 Compare May 22, 2026 04:42
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review: PR #741 — Regenerate cpflow review-app workflows

Overview

This PR replaces all local composite actions (cpflow-build-docker-image, cpflow-delete-control-plane-app, cpflow-detect-release-phase, cpflow-setup-environment, cpflow-validate-config, cpflow-wait-for-health) with calls to upstream reusable workflows in shakacode/control-plane-flow. The result is a dramatic simplification of local workflow YAML (~1,800 lines deleted, ~30 added), but that simplicity comes with meaningful trade-offs.


Critical: Mutable upstream ref

Every workflow in this PR uses the same external ref:

uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions

jg-codex/reusable-github-actions is a branch, not an immutable tag or commit SHA. This means:

  • Any force-push to that branch immediately changes what this repo runs in production and review-app deployments.
  • There is no pinning — the workflow you test today is not guaranteed to be the workflow that runs tomorrow.
  • GitHub's own security hardening guide recommends SHA-pinning for third-party workflow refs precisely to prevent this class of supply-chain risk.

Recommendation: Before merging, pin to a commit SHA or a semantically versioned tag (e.g. @v1.2.3). If this branch is expected to track a moving target during active development, establish a plan for pinning before the branch is merged upstream.


High: secrets: inherit passes all repository secrets to external code

Every caller passes secrets: inherit:

secrets: inherit

This grants the upstream reusable workflow access to all repository secrets — including CPLN_TOKEN_STAGING, CPLN_TOKEN_PRODUCTION, DOCKER_BUILD_SSH_KEY, and GITHUB_TOKEN. Combined with the mutable ref above, a single pushed commit to the upstream branch could exfiltrate production credentials.

Even with a pinned ref, secrets: inherit should be used only when the upstream repo's code is fully trusted and auditable. That bar is higher when the implementation is no longer locally visible in this repo.

Recommendation: Explicitly enumerate the secrets each workflow needs (secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}) rather than inheriting all of them. This limits blast radius if upstream code changes.


Medium: Permissions broadened on the promote workflow

Before:

permissions:
  contents: read
# separate job had: permissions: contents: write

After:

permissions:
  contents: write

The contents: write permission is now set at the workflow level and passed to the reusable workflow, which means the upstream code runs with write access to repository contents for the entire promote job — not just the GitHub release creation step. Least-privilege is the safer posture here.


Medium: Concurrency controls removed from caller side

The previous workflows each had explicit concurrency: blocks with cancel-in-progress: false to prevent race conditions during deploy, delete, and cleanup operations. Those blocks are gone from the caller side. If the upstream reusable workflows don't replicate these settings, concurrent runs could race and leave workloads in partially-deployed states.

Recommendation: Verify that the upstream workflows define appropriate concurrency groups, or add concurrency controls back to the caller workflows.


Low: Security guidance comment removed

In cpflow-review-app-help.yml, a comment explaining why pull_request_target is safe and warning against adding checkout steps was shortened from:

"Do not switch this to pull_request or add a checkout step without re-evaluating."

to just:

"Do not switch this to pull_request."

The checkout-step warning was the more actionable part of the guidance. Future contributors won't see it when reading this file.


Observation: Testing is inherently post-merge

The PR description acknowledges that comment-triggered workflows (+review-app-deploy etc.) load from the default branch, so meaningful end-to-end testing only happens after merging. This is a general limitation of this workflow architecture, not introduced by this PR, but worth flagging for reviewers: approving this PR is approving code that cannot be fully tested in a staging branch context.


Summary

Concern Severity
Mutable branch ref for external workflows Critical
secrets: inherit to external mutable code High
contents: write scoped too broadly Medium
Concurrency controls may be silently dropped Medium
Security comment removed Low

The approach of delegating to upstream reusable workflows is reasonable as a long-term architecture, but the current implementation should be tightened — at minimum, pin the upstream ref to an immutable SHA or tag before merging.

uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions
with:
control_plane_flow_ref: jg-codex/reusable-github-actions
secrets: inherit
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Critical: mutable branch ref

@jg-codex/reusable-github-actions is a branch, not a tag or immutable commit SHA. Any force-push to that branch immediately changes what runs in production and review-app deployments — including steps that receive secrets: inherit.

Pin to an immutable SHA or a semantically versioned tag before merging:

Suggested change
secrets: inherit
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@<commit-sha-or-vX.Y.Z>

permissions:
contents: read

concurrency:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium: concurrency controls removed

This concurrency: block (with cancel-in-progress: false) prevented overlapping cleanup sweeps that could leave review apps in a half-deleted state. It's removed from all caller workflows in this PR.

Before merging, verify that the upstream reusable workflows replicate equivalent concurrency groups — or add concurrency: back to the caller side. A missing concurrency guard here is a correctness issue, not just a performance one.

# checked out — the job only calls `actions/github-script` with a hardcoded message.
# Do not switch this to `pull_request` or add a checkout step without re-evaluating.
# checked out — the job only calls the upstream reusable workflow with a hardcoded
# message.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The previous comment included an explicit warning: "Do not switch this to pull_request or add a checkout step without re-evaluating." The checkout-step part of that guidance is the most actionable and was removed in this edit. Consider restoring it so future contributors understand the trust boundary.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37222b29b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

gh release create "${release_tag}" \
--title "Production Release ${release_date} ${timestamp}" \
--notes "Promoted ${STAGING_APP_NAME} to ${PRODUCTION_APP_NAME} on ${release_date} at ${timestamp}."
uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@jg-codex/reusable-github-actions
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pin production reusable workflow to immutable SHA

Reference the called workflow by full commit SHA instead of the mutable branch @jg-codex/reusable-github-actions. This production promotion job now executes whatever code that branch points to at runtime while inheriting repository secrets, so a force-push or branch deletion in shakacode/control-plane-flow can either run unexpected deployment logic or break promotions entirely; pinning to an immutable revision restores deterministic and auditable behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Pinned upstream feature branch ref
    • Updated all cpflow reusable workflow callers and control_plane_flow_ref inputs from the feature branch to the upstream main branch.

Create PR

Or push these changes by commenting:

@cursor push e92e7b1ec9
Preview (e92e7b1ec9)
diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml
--- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml
+++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml
@@ -10,7 +10,7 @@
 
 jobs:
   cleanup:
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@main
     with:
-      control_plane_flow_ref: jg-codex/reusable-github-actions
+      control_plane_flow_ref: main
     secrets: inherit

diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml
--- a/.github/workflows/cpflow-delete-review-app.yml
+++ b/.github/workflows/cpflow-delete-review-app.yml
@@ -26,7 +26,7 @@
        contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
       (github.event_name == 'pull_request_target' && github.event.action == 'closed') ||
       github.event_name == 'workflow_dispatch'
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@main
     with:
-      control_plane_flow_ref: jg-codex/reusable-github-actions
+      control_plane_flow_ref: main
     secrets: inherit

diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml
--- a/.github/workflows/cpflow-deploy-review-app.yml
+++ b/.github/workflows/cpflow-deploy-review-app.yml
@@ -30,7 +30,7 @@
        github.event.issue.pull_request &&
        contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) &&
        contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@main
     with:
-      control_plane_flow_ref: jg-codex/reusable-github-actions
+      control_plane_flow_ref: main
     secrets: inherit

diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml
--- a/.github/workflows/cpflow-deploy-staging.yml
+++ b/.github/workflows/cpflow-deploy-staging.yml
@@ -16,8 +16,8 @@
 
 jobs:
   deploy-staging:
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@main
     with:
-      control_plane_flow_ref: jg-codex/reusable-github-actions
+      control_plane_flow_ref: main
       staging_app_branch_default: ""
     secrets: inherit

diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml
--- a/.github/workflows/cpflow-help-command.yml
+++ b/.github/workflows/cpflow-help-command.yml
@@ -23,4 +23,4 @@
        contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) &&
        contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
       github.event_name == 'workflow_dispatch'
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@main

diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml
--- a/.github/workflows/cpflow-promote-staging-to-production.yml
+++ b/.github/workflows/cpflow-promote-staging-to-production.yml
@@ -14,7 +14,7 @@
 jobs:
   promote-to-production:
     if: github.event.inputs.confirm_promotion == 'promote'
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@main
     with:
-      control_plane_flow_ref: jg-codex/reusable-github-actions
+      control_plane_flow_ref: main
     secrets: inherit

diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml
--- a/.github/workflows/cpflow-review-app-help.yml
+++ b/.github/workflows/cpflow-review-app-help.yml
@@ -15,4 +15,4 @@
 jobs:
   show-help:
     if: vars.REVIEW_APP_PREFIX != ''
-    uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@jg-codex/reusable-github-actions
+    uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@main

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 37222b2. Configure here.

Comment thread .github/workflows/cpflow-deploy-review-app.yml Outdated
@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review: Regenerate cpflow review app feedback

Overview

This PR consolidates ~1,800 lines of inline CI/CD logic (6 composite actions + in-workflow steps) into thin delegating wrappers that call pinned upstream reusable workflows from shakacode/control-plane-flow. The SHA pinning (@2db67abcfc0041ec155203f81d19d28aaeb784b5) is the correct approach for supply-chain hygiene. The simplification is real and the help-doc changes are accurate.

Several items below warrant attention before merging.


Security: secrets: inherit passes every repository secret to the upstream

All six delegating workflows now include secrets: inherit. With local step implementations, only the secrets explicitly named (e.g. CPLN_TOKEN_STAGING, DOCKER_BUILD_SSH_KEY) were passed to each composite action. With secrets: inherit, all repository secrets — including secrets unrelated to Control Plane (database credentials, API tokens, signing keys, etc.) — are forwarded to every reusable workflow in shakacode/control-plane-flow.

This is the standard pattern for reusable workflows in the same org, but it is worth a conscious decision: if the upstream repo is ever compromised or a workflow bug surfaces, it would have access to the full secret surface. Consider whether you can enumerate only the secrets each workflow actually needs, or at minimum document the accepted trade-off.


Permissions broadening in cpflow-promote-staging-to-production.yml

permissions:
  contents: write   # was: contents: read

The old file had two jobs: promote-to-production (contents: read) and a separate create-github-release job scoped to contents: write. The new file applies contents: write at the top-level workflow, which now covers the whole job. The permission is still needed for GitHub release creation, but the elevation is now broader than required. If the upstream reusable workflow accepts a caller-provided permission through its design, you may want to add a comment here explaining why write is required, so future reviewers don't reduce it back to read by accident.


Removed concurrency blocks — verify upstream equivalents exist

The previous workflows had explicit concurrency groups with cancel-in-progress: false for every deploy/delete/cleanup path:

  • cpflow-deploy-review-app: group: cpflow-review-app-<pr>, no cancel
  • cpflow-delete-review-app: group: cpflow-delete-review-app-<pr>, no cancel
  • cpflow-deploy-staging: group: cpflow-deploy-staging-<ref>, no cancel
  • cpflow-cleanup-stale-review-apps: group: cpflow-cleanup-stale-review-apps, no cancel
  • cpflow-promote-staging-to-production: group: cpflow-promote-staging-to-production, no cancel

These guards prevented a cancelled mid-flight deploy from leaving a GVC in a partially-updated state. Please confirm that the upstream reusable workflows at 2db67ab implement equivalent concurrency blocks, or note the risk in the PR description. A half-deployed production promotion with no rollback guard is the worst-case scenario here.


PR description inconsistency: cpflow-setup-environment is deleted, not kept

The description reads:

I intentionally kept the existing downstream cpflow-setup-environment action unchanged because upstream PR #297 is still open

However the diff deletes .github/actions/cpflow-setup-environment/action.yml entirely. This is logically consistent (the local workflows no longer reference any local composite actions, so cpflow-setup-environment is dead code), but the PR description is misleading. Consider updating the wording to: "all local composite actions are deleted because they are no longer referenced; the upstream cpflow-setup-environment template was not regenerated locally before deletion."


staging_app_branch_default: "" — confirm expected behavior

cpflow-deploy-staging.yml passes staging_app_branch_default: "" as an explicit input to the upstream workflow. An empty string could mean "use the upstream's own default" or "disable branch-override logic and always deploy." Verify what the upstream workflow does when this input is empty, and if "" is the right sentinel for "no override," add a comment here explaining the intent.

# Don't cancel an in-flight promotion: a half-finished `cpflow deploy-image` plus a
# rollback can leave production in a worse state than letting the first run finish.
cancel-in-progress: false
contents: write
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The old file scoped contents: write to just the create-github-release job. This top-level declaration now grants write access for the entire workflow (including the production promotion job). The permission is necessary for GitHub release creation, but a comment explaining the reason would prevent a future reviewer from narrowing it back to read.

Suggested change
contents: write
contents: write # required for gh release create in the upstream promote workflow

uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@2db67abcfc0041ec155203f81d19d28aaeb784b5
with:
control_plane_flow_ref: 2db67abcfc0041ec155203f81d19d28aaeb784b5
secrets: inherit
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

secrets: inherit forwards every repository secret to the upstream reusable workflow, not just the Control Plane tokens. This is the broadest possible grant. For a production promotion workflow this is especially sensitive — if the upstream is ever compromised all secrets (staging token, production token, any other repo secrets) are exposed.

If the upstream workflow documents which secrets it consumes, consider whether it supports explicit secret mappings instead, or add a comment acknowledging the accepted surface.

uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@2db67abcfc0041ec155203f81d19d28aaeb784b5
with:
control_plane_flow_ref: 2db67abcfc0041ec155203f81d19d28aaeb784b5
staging_app_branch_default: ""
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What does the upstream workflow do when staging_app_branch_default is ""? If it means "no custom branch — use main/master," the empty string is correct but the intent isn't obvious. A brief comment would help:

Suggested change
staging_app_branch_default: ""
staging_app_branch_default: "" # empty = no override; upstream defaults to main/master

@justin808 justin808 force-pushed the jg-codex/animated-review-app-feedback branch from 25d1f36 to 6b5760d Compare May 22, 2026 08:44
# Don't cancel an in-flight promotion: a half-finished `cpflow deploy-image` plus a
# rollback can leave production in a worse state than letting the first run finish.
cancel-in-progress: false
contents: write
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Permission scope expandedcontents: write was previously narrowly scoped to the create-github-release job only. Now it's granted at the top-level workflow, so the promote-to-production job also runs with contents: write.

GitHub doesn't let a calling workflow restrict permissions below what a reusable workflow declares — but it can be no broader than what the caller grants here. If the upstream reusable workflow actually needs contents: write only for the release step, consider whether the promotion job itself should have that permission. Verify the upstream workflow's permissions: block to confirm this isn't unnecessarily broad.

issue_number: prNumber,
body: helpText
});
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@8e9c0c5e9991ac8651ae2721830bf5231f34de5c
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Inconsistent with other delegating workflows — Five of the seven refactored workflows pass control_plane_flow_ref via with:, but this one (and cpflow-review-app-help.yml) omit the with: block entirely.

If the upstream workflow accepts a control_plane_flow_ref input, omitting it here means any sub-workflows it calls might not be pinned to the same SHA. If it's genuinely not needed for these two workflows, a brief comment explaining why would prevent future readers from adding it unnecessarily — or missing a real gap.

@claude
Copy link
Copy Markdown

claude Bot commented May 22, 2026

Code Review

Overview

This PR replaces ~1,800 lines of in-repo composite actions and workflow steps with thin delegating wrappers that call SHA-pinned reusable workflows from shakacode/control-plane-flow. The pattern is sound and the reduction in locally-maintained CI/CD logic is a real benefit.


Strengths

  • SHA pinning (8e9c0c5e9991ac8651ae2721830bf5231f34de5c) on all upstream uses: references is the right supply-chain practice.
  • Removing hundreds of lines of locally-owned rollback, health-check, Docker-SSH, and config-validation logic is a genuine maintenance win — these were complex and hard to keep in sync.
  • The decision to deliberately hold cpflow-setup-environment at its current version (pending upstream PR Broken link in readme file #297) is well-documented and avoids introducing a known regression.
  • The help doc update (REVIEW_APP_DEPLOYING_ICON_URL, revised testing guidance) is accurate and useful.

Concerns

1. secrets: inherit passed to an external org's reusable workflow

All seven refactored workflows use secrets: inherit, which forwards every secret in the calling repo (staging tokens, production tokens, SSH keys, GITHUB_TOKEN) to the upstream reusable workflow. SHA-pinning narrows the blast radius to the specific commit, but:

  • Any secret added to this repo in the future will automatically be available to the upstream code at that SHA.
  • If the upstream repo is ever compromised and a new SHA is cut, a single Renovate/Dependabot bump would silently forward all secrets to the attacker's code.

Consider whether secrets: inherit is necessary for every workflow or whether explicit secret forwarding (e.g., secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}) would be feasible. For the help and review-app-help workflows in particular, it's worth asking whether they need production-level secrets at all.

2. concurrency groups removed without confirmation they're re-added upstream

The original workflows had carefully designed concurrency blocks with cancel-in-progress: false specifically to prevent partial deployments (a half-deployed workload, a half-deleted review app). Those blocks were removed here, and it's not visible from this diff whether the upstream reusable workflows re-establish equivalent concurrency controls. If they don't, concurrent pushes to the same PR could race during Docker build or cpflow deploy-image.

Worth confirming the upstream workflows have equivalent concurrency: blocks, and ideally linking to the relevant section in the upstream source.

3. contents: write scope expansion in the promotion workflow

See inline comment on cpflow-promote-staging-to-production.yml line 12.

4. Inconsistent control_plane_flow_ref parameter

See inline comment on cpflow-help-command.yml line 26. cpflow-review-app-help.yml has the same gap.


Minor

  • The staging_app_branch_default: "" input added to cpflow-deploy-staging.yml — an empty string as a default. Confirm the upstream workflow treats "" the same as "absent" (i.e., falls back to main/master), rather than treating it as a literal empty branch name that would never match.
  • The PR description notes that merging to master is required before comment-triggered runs will pick up the new workflow code (because issue_comment workflows load from the default branch). This is correct and worth highlighting in the merge checklist to avoid confusion if reviewers test by commenting on a PR before merge.

@justin808 justin808 merged commit ab025f9 into master May 22, 2026
11 checks passed
@justin808 justin808 deleted the jg-codex/animated-review-app-feedback branch May 22, 2026 08:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant