Skip to content
Draft
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
25f8094
Add GitHub Actions flow generator
justin808 Apr 15, 2026
2f6a434
Harden generated GitHub flow workflows
justin808 Apr 15, 2026
8a5e4d8
Skip review app deploys until repo is configured
justin808 Apr 15, 2026
9215b4c
Fix external GitHub flow generator execution
justin808 Apr 15, 2026
b94b0e1
Stabilize latest image integration spec
justin808 Apr 15, 2026
8787015
Support SSH-backed Docker builds in GitHub flow
justin808 Apr 15, 2026
501e4c6
Fix generator spec Rubocop memoization
justin808 Apr 15, 2026
c983f12
Harden generated GitHub flow workflows
justin808 Apr 15, 2026
746e476
Validate staging config only on deployable refs
justin808 Apr 15, 2026
ecc4335
Document GitHub flow readiness checks
justin808 Apr 15, 2026
2f44d27
Support JS package managers in generated Dockerfile
justin808 Apr 15, 2026
cc0c6a6
Infer Ruby version for generated Dockerfile
justin808 Apr 15, 2026
9233492
Generate review-app ready Rails scaffolds
justin808 Apr 15, 2026
38a8155
Tighten generated GitHub flow scaffolds
justin808 Apr 15, 2026
6973c9e
Document AI rollout prompt and stop conditions
justin808 Apr 15, 2026
2f8ce1d
Add AI rollout prompt command and local bootstrap mode
justin808 Apr 15, 2026
b95a011
Add GitHub flow readiness command
justin808 Apr 15, 2026
20a6a27
Fix shorthand RubyGems readiness checks
justin808 Apr 15, 2026
2e2ca24
Harden generated GitHub flow scaffolding
justin808 Apr 15, 2026
901d088
Document optional Docker build inputs in help workflow
justin808 Apr 15, 2026
2890bf3
Preserve frontend precompile hooks in generated Dockerfiles
justin808 Apr 15, 2026
e58af0f
Tighten Docker build arg handling in GitHub flow
justin808 Apr 15, 2026
46b6fdc
Harden GitHub flow generation and readiness checks
justin808 Apr 18, 2026
9519b7b
Address PR review feedback on GitHub flow generator
justin808 Apr 19, 2026
9a6305c
Sync docs/commands.md with updated copy-image-from-upstream help text
justin808 Apr 19, 2026
6f53828
Address review feedback: correctness, robustness, and dedup
justin808 Apr 23, 2026
46c4871
Fix rollback guard and dedupe SQLite template copies
justin808 Apr 23, 2026
2daf69b
Address PR #278 bot review feedback: security, silent failures, consi…
justin808 Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 43 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@ _If you need a free demo account for Control Plane (no CC required), you can con

---

Be sure to see the [demo app](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane), which includes simple YAML configurations and setup for `cpflow`.
To bootstrap a new project, run three commands from the repo root:

Also, check [how the `cpflow` gem (this project) is used in the Github actions](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.github/actions/deploy-to-control-plane/action.yml).
1. **`cpflow github-flow-readiness`** — gate for common rollout blockers (missing Rails runtime scaffold, legacy Ruby or Bundler toolchains, unpublished exact-pinned gems or npm packages, missing production Dockerfiles). Exits non-zero on any blocker.
2. **`cpflow generate`** — creates `.controlplane/` scaffolding. Infers the app prefix from the repo directory, the base Ruby version from `.ruby-version`/`.tool-versions`/`Gemfile`, the JS package manager from `package.json`, repo-defined frontend precompile hooks (Shakapacker `precompile_hook`, React on Rails auto bundle generation), and switches to persistent `db` + `storage` volume templates when `config/database.yml` shows SQLite in production.
3. **`cpflow generate-github-actions`** — adds the reusable GitHub Actions pipeline for review apps, staging deploys, and manual production promotion.

The generated scaffold is a starting point. After generation, adapt `.controlplane/` for app-specific workloads (Sidekiq, Node renderer), wire any private-dependency Docker build settings (SSH key, optional known-host overrides), and verify that the production Docker build succeeds.

See [CI automation](./docs/ci-automation.md) for the full setup and required GitHub secrets and variables. For an AI agent rollout, see the [AI rollout prompt](./docs/ai-github-flow-prompt.md) or run `cpflow ai-github-flow-prompt` inside the target repo to print a copy-paste prompt with that repo's default app prefix filled in.

For a live reference, see the [demo app](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane) and its [GitHub Actions flow](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.github).
Here is a brief [video overview](https://www.youtube.com/watch?v=llaQoAV_6Iw).

---
Expand Down Expand Up @@ -64,6 +72,7 @@ Additionally, the documentation includes numerous examples and practical tips fo
- Extensive Heroku-to-Control Plane migration examples included in the documentation.
- Convention-driven configuration to simplify workflows and reduce custom scripting requirements.
- Easy to understand Heroku to Control Plane conventions in setup and naming.
- GitHub Actions generator for on-demand review apps, automatic staging deploys, and manual promotion to production.
- **Safe, production-ready** equivalents of `heroku run` and `heroku run:detached` for Control Plane.
- Automatic sequential release tagging for Docker images.
- A project-aware CLI that enables working on multiple projects.
Expand Down Expand Up @@ -118,7 +127,9 @@ _Note, if you want to use Terraform with cpflow, you will start the same way bel

3. Install [Ruby](https://www.ruby-lang.org/en/) (required for these helpers).

4. Install Control Plane CLI, and configure access ([docs here](https://shakadocs.controlplane.com/quickstart/quick-start-3-cli#getting-started-with-the-cli)).
4. Purely local bootstrap commands can run before Control Plane CLI is installed. That includes `cpflow help`, `cpflow version`, `cpflow github-flow-readiness`, `cpflow ai-github-flow-prompt`, `cpflow generate`, `cpflow generate-github-actions`, and `cpflow terraform generate`. Install Control Plane CLI before any `cpflow` command that talks to Control Plane infrastructure.

5. Install Control Plane CLI, and configure access ([docs here](https://shakadocs.controlplane.com/quickstart/quick-start-3-cli#getting-started-with-the-cli)).

```sh
# Install CLI
Expand All @@ -131,11 +142,11 @@ cpln login
npm update -g @controlplane/cli
```

5. Run `cpln image docker-login --org <your-org>` to ensure that you have access to the Control Plane Docker registry.
6. Run `cpln image docker-login --org <your-org>` to ensure that you have access to the Control Plane Docker registry.

6. Install Control Plane Flow `cpflow` CLI as a [Ruby gem](https://rubygems.org/gems/cpflow): `gem install cpflow`. If you want to use `cpflow` from Rake tasks in a Rails project, use `Bundler.with_unbundled_env { `cpflow help` } or else you'll get an error that `cpflow` cannot be found. While you can add `cpflow` to your Gemfile, it's not recommended because it might trigger conflicts with other gems.
7. Install Control Plane Flow `cpflow` CLI as a [Ruby gem](https://rubygems.org/gems/cpflow): `gem install cpflow`. If you want to use `cpflow` from Rake tasks in a Rails project, use `Bundler.with_unbundled_env { `cpflow help` } or else you'll get an error that `cpflow` cannot be found. While you can add `cpflow` to your Gemfile, it's not recommended because it might trigger conflicts with other gems.

7. You will need a production-ready Dockerfile. If you're using Rails, consider the default one that ships with Rails 8. You can use [this Dockerfile](https://github.com/shakacode/rails-v8-kamal-v2-terraform-gcp-tutorial/blob/master/Dockerfile) as an example for your project. Ensure that you have Docker running.
8. You will need a production-ready Dockerfile. If you're using Rails, consider the default one that ships with Rails 8. You can use [this Dockerfile](https://github.com/shakacode/rails-v8-kamal-v2-terraform-gcp-tutorial/blob/master/Dockerfile) as an example for your project. Ensure that you have Docker running.

**Note:** Do not confuse the `cpflow` CLI with the `cpln` CLI. The `cpflow` CLI is the Control Plane Flow playbook CLI.
The `cpln` CLI is the Control Plane CLI.
Expand All @@ -148,20 +159,22 @@ The `cpflow` gem is based on several configuration files within a `/.controlplan
.controlplane/
├─ templates/
│ ├─ app.yml
│ ├─ postgres.yml
│ ├─ postgres.yml or db.yml + storage.yml
│ ├─ rails.yml
├─ controlplane.yml
├─ Dockerfile
├─ entrypoint.sh
├─ release_script.sh
```

1. `controlplane.yml` describes the overall application. Be sure to have `<your-org>` as the value for `aliases.common.cpln_org`, or set it with the `CPLN_ORG` environment variable.
2. `Dockerfile` builds the production application. `entrypoint.sh` is an _example_ entrypoint script for the production application, referenced in your Dockerfile.
3. `templates` directory contains the templates for the various workloads, such as `rails.yml` and `postgres.yml`.
4. `templates/app.yml` defines your project's GVC (like a Heroku app). More importantly, it contains ENV values for the app.
5. `templates/rails.yml` defines your Rails workload. It may inherit ENV values from the parent GVC, which is populated from the `templates/app.yml`. This file also configures scaling, sizing, firewalls, and other workload-specific values.
6. For other workloads (like lines in a Heroku `Procfile`), you create additional template files. For example, you can base a `templates/sidekiq.yml` on the `templates/rails.yml` file.
7. You can have other files in the `templates` directory, such as `redis.yml` and `postgres.yml`, which could setup Redis and Postgres for a testing application.
1. `controlplane.yml` describes the overall application. The generated version includes staging, review, and production entries named from the repo directory, plus `setup_app_templates` and `release_script` defaults. Be sure to update `<your-org>` as the value for `aliases.common.cpln_org`, or set it with the `CPLN_ORG` environment variable.
2. `Dockerfile` builds the production application. The generated example includes Node.js, package-manager auto-detection (`npm`, Yarn, or `pnpm`), a Ruby base-image hint derived from `.ruby-version`, `.tool-versions`, or the app's `Gemfile`, and any detected repo-defined asset precompile hook such as a Shakapacker `precompile_hook` or React on Rails auto bundle generation step so Rails apps with frontend assets can precompile from a clean clone. `entrypoint.sh` is an _example_ entrypoint script for the production application, referenced in your Dockerfile.
3. `release_script.sh` is the generated release-phase entrypoint used by `cpflow deploy-image --run-release-phase` and `cpflow promote-app-from-upstream --run-release-phase`.
4. `templates` directory contains the templates for the various workloads, such as `rails.yml` and either `postgres.yml` or the SQLite `db.yml` and `storage.yml` pair.
5. `templates/app.yml` defines your project's GVC (like a Heroku app). More importantly, it contains ENV values for the app.
6. `templates/rails.yml` defines your Rails workload. It may inherit ENV values from the parent GVC, which is populated from the `templates/app.yml`. This file also configures scaling, sizing, firewalls, and other workload-specific values.
7. For other workloads (like lines in a Heroku `Procfile`), you create additional template files. For example, you can base a `templates/sidekiq.yml` on the `templates/rails.yml` file.
8. You can have other files in the `templates` directory, such as `redis.yml`, `postgres.yml`, or SQLite-backed `db.yml` and `storage.yml`, depending on the application runtime.

Here's a complete example of all supported config keys explained for the `controlplane.yml` file:

Expand Down Expand Up @@ -330,6 +343,21 @@ apps:

## Workflow

### Bootstrap a New Repo

```sh
# Check the repo for common rollout blockers before generating files
cpflow github-flow-readiness

# Create the .controlplane/ scaffolding
cpflow generate

# Create reusable GitHub Actions for review apps, staging, and production promotion
cpflow generate-github-actions
```

`cpflow github-flow-readiness` exits non-zero when it finds blockers such as unpublished exact-pinned packages or a missing production Dockerfile, so use it as the gate before generation. Then review the generated `.controlplane/controlplane.yml` entries, adjust any app-specific workloads, and configure the GitHub repository variables and secrets described in [CI automation](./docs/ci-automation.md), including the optional Docker build settings for private GitHub dependencies and custom SSH known hosts. `cpflow generate` already switches to persistent `db` and `storage` volumes when `config/database.yml` shows SQLite in production and preserves detected frontend precompile hooks, but you should still confirm that the generated Dockerfile picked a Ruby base image compatible with the app's declared Ruby requirement and that the emitted workload set matches the real app. If you want an AI agent to do this end to end, start with the [AI rollout prompt](./docs/ai-github-flow-prompt.md) or run `cpflow ai-github-flow-prompt` in the target repo rather than giving a vague "set up CI" request.

For a live example, see the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/readme.md) repository.

You can use this repository as a reference for setting up your own project.
Expand Down Expand Up @@ -387,7 +415,7 @@ cpflow build-image -a tutorial-app --commit ABCD

### Real World

Most companies will configure their CI system to handle the above steps. Please [contact Shakacode](mailto:controlplane@shakacode.com) for examples of how to do this.
Most teams will automate the above steps in CI. Run `cpflow generate-github-actions` to scaffold the GitHub Actions flow, then follow [CI automation](./docs/ci-automation.md) to wire it to your `.controlplane/controlplane.yml` and repository settings.

You can also join our [**Slack channel**](https://reactrails.slack.com/join/shared_invite/enQtNjY3NTczMjczNzYxLTlmYjdiZmY3MTVlMzU2YWE0OWM0MzNiZDI0MzdkZGFiZTFkYTFkOGVjODBmOWEyYWQ3MzA2NGE1YWJjNmVlMGE) for ShakaCode open source projects.

Expand Down
61 changes: 61 additions & 0 deletions docs/ai-github-flow-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# AI Rollout Prompt for Control Plane GitHub Flow

Use this file when you want an AI agent to add the reusable `cpflow` review-app,
staging, and production-promotion flow to a repository.

If `cpflow` is already installed in the target repo, you can print the current
copy-paste version of this prompt with:

```sh
cpflow ai-github-flow-prompt
```

That local-only command works even before `cpln` is installed and fills in the
repo-name default app prefix for the current checkout. You can also run
`cpflow github-flow-readiness` first to check the same blocker categories the
prompt tells the agent to stop on.

## Recommended Prompt

```text
Set up Control Plane GitHub Flow for this repo. Start with `cpflow github-flow-readiness` and stop on any reported blockers. The repo must be deployable from a clean clone: published package versions, complete runtime scaffold, and a production Dockerfile that can build the app. If any package version is unpublished, inaccessible from CI, or requires credentials that are not already modeled in the repo or GitHub settings, stop and report the blocker instead of generating workflow files. If the repo is a legacy sample pinned to an obsolete Ruby or Bundler toolchain, if it does not even have a production Dockerfile yet, or if it is a monorepo without an already-decided single app boundary for this flow, stop and report that as a prerequisite instead of forcing the rollout.

If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default and rename them only if the project needs a different prefix. Then run `cpflow generate-github-actions`, keep review apps opt-in via `/deploy-review-app`, use `STAGING_APP_BRANCH` or the default branch for staging deploys, and list the GitHub secrets and variables that must be configured.

Keep Node available in the final image if asset compilation or SSR depends on ExecJS, Yarn, `pnpm`, or npm after the main install layer. Make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby requirement. Preserve repo-defined frontend build hooks: if `config/shakapacker.yml` defines a `precompile_hook`, or React on Rails enables `config.auto_load_bundle = true`, confirm the generated Dockerfile runs that codegen step before `rails assets:precompile`. If `config/database.yml` shows SQLite in production, confirm that the generated scaffold uses persistent `db` and `storage` volumes plus a release script that runs `rails db:prepare`; otherwise keep the default Postgres workload. If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated workflows. Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to newline-delimited single tokens such as `--build-arg=FOO=bar`.

Run the real local validations you can: Docker build if feasible, repo tests or smoke checks, YAML validation, and any CI-equivalent build steps. Push the branch and check the GitHub Actions results. Only stop early for a real external blocker or a product decision that changes scope.
```

## Hard Stop Conditions

Stop and report the blocker instead of generating `cpflow-*` workflow files when:

- the repo is a partial sample or generator snapshot rather than a deployable app
- the app depends on unpublished or inaccessible gem or npm package versions
- the repo is pinned to a legacy Ruby or Bundler toolchain that you cannot validate in the current environment
- there is no production Dockerfile and the app's production build path is still undefined
- the repo is a monorepo or contains multiple deployable apps and the flow target is not already decided
- the local checkout does not match the intended remote repository
- the app needs product decisions about workload shape, secrets, or promotion behavior that are not already implied by the repo

## Definition of Done

The rollout is done when all of the following are true:

- `.controlplane/` exists and matches the actual app shape
- `.github/actions/cpflow-*` and `.github/workflows/cpflow-*` are in place
- review apps are opt-in, staging auto-deploys from one branch, and production promotion is manual
- required GitHub secrets and variables are documented for the repo
- the production image build path is validated for the real app
- repo-specific runtime concerns are handled, such as SQLite volumes, sidekiq workloads, SSR runtime Node access, React on Rails pack generation hooks, or private dependency fetches
- the branch is pushed and the relevant GitHub checks are either green or blocked only by an external system failure

## React on Rails Notes

For React on Rails and React on Rails Pro apps, explicitly verify:

- SSR or renderer workloads do not lose Node or package-manager access in the final image
- sidecar renderers or worker processes bind to `0.0.0.0`, not container-local `localhost`
- writable caches, bundle outputs, or SQLite files live in runtime-writable paths
- old demo repos are treated as legacy exceptions unless they can still build from a clean clone
Loading
Loading