Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ _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 `cpflow generate` to create the `.controlplane/` scaffolding and `cpflow generate-github-actions` to add the reusable GitHub Actions pipeline for review apps, staging deploys, and manual production promotion. Then adapt `.controlplane/` for app-specific workloads like Sidekiq or a Node renderer and verify that the production Docker build succeeds. See [CI automation](./docs/ci-automation.md) for the full setup.

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).
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 +64,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 @@ -330,6 +331,18 @@ apps:

## Workflow

### Bootstrap a New Repo

```sh
# Create the .controlplane/ scaffolding
cpflow generate

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

Then update `.controlplane/controlplane.yml` so the app defines staging, review, and production entries, and configure the GitHub repository variables and secrets described in [CI automation](./docs/ci-automation.md).

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 +400,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
225 changes: 197 additions & 28 deletions docs/ci-automation.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,197 @@
# CI Automation, Review Apps, Staging, and Promoting to Production

## Setting up Tokens for CI Automation

This example uses Github Actions. The same applies to Circle CI and other similar CI/CD tools.

1. Ensure that you have two orgs:
1. `company-staging` (for staging deployments, developers have access)
2. `company-production` (for production deployments, limited access)
2. Create the token for staging org and set on Github repository secrets and variables:
1. Go to the Control Plane UI for your organization's staging org
2. Make a new service account called `github-actions-staging`
3. Assign to the group `superusers`
4. Click "Keys" and create a one with description "Github Actions" and copy the token (or download it).
5. Add this key to your Github repository **secrets** as `CPLN_TOKEN_STAGING`
6. Add another key to your Github repository **variables** as `CPLN_ORG_STAGING` with the name of the staging org, like `company-staging`
3. Create the token for production org, and set on Github repository secrets and variables.
1. Go to the Control Plane UI for your organization's production org
2. Make a new service account called `github-actions-production`
3. Assign to the group `superusers`
4. Click "Keys" and create a one with description "Github Actions" and copy the token (or download it).
5. Add this key to your Github repository **secrets** as `CPLN_TOKEN_PRODUCTION`
6. Add another key to your Github repository **variables** as `CPLN_ORG_PRODUCTION` with the name of the production org, like `company-production`
4. Create a few more ENV **variables** for the app name and the app prefix:
1. `STAGING_APP_NAME` - the name of the app in Control Plane for staging, which is the GVC name, like `app-name-staging`
2. `PRODUCTION_APP_NAME` - the name of the app in Control Plane for production, which is the GVC name, like `app-name-production`
3. `REVIEW_APP_PREFIX` - the prefix for the review apps in Control Plane. The Review apps are named `$REVIEW_APP_PREFIX-pr-$PR_NUMBER`
5. All in all, you should have 2 secrets and 5 variables set in your Github repository
# GitHub Actions Flow for Review Apps, Staging, and Production

This document describes the reusable GitHub Actions scaffolding generated by `cpflow generate-github-actions`.

The goal is to bring the Heroku Flow model into any `cpflow` project:

1. Comment `/deploy-review-app` on a pull request to create or update a review app.
2. Push more commits to the PR to auto-redeploy that review app.
3. Push to the staging branch to auto-deploy staging.
4. Promote the already-built staging artifact to production from the Actions tab.
5. Let a nightly workflow clean up stale review apps.

## Bootstrap a Project

Run these commands from the project root:

```sh
# Create .controlplane/ if it does not exist yet
cpflow generate

# Add reusable GitHub Actions for the Control Plane flow
cpflow generate-github-actions
```

The second command writes namespaced files so they can coexist with an app's existing CI:

- `.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`
- `.github/workflows/cpflow-review-app-help.yml`
- `.github/workflows/cpflow-help-command.yml`
- `.github/workflows/cpflow-deploy-review-app.yml`
- `.github/workflows/cpflow-delete-review-app.yml`
- `.github/workflows/cpflow-deploy-staging.yml`
- `.github/workflows/cpflow-promote-staging-to-production.yml`
- `.github/workflows/cpflow-cleanup-stale-review-apps.yml`

## Required `.controlplane/controlplane.yml` Structure

The generated workflows assume that `.controlplane/controlplane.yml` defines:

- one staging app
- one review-app prefix with `match_if_app_name_starts_with: true`
- one production app with `upstream` pointing to staging

Typical shape:

```yaml
aliases:
common: &common
cpln_org: my-org-staging
default_location: aws-us-east-2
setup_app_templates:
- app
- postgres
- redis
- rails
app_workloads:
- rails
additional_workloads:
- postgres
- redis

apps:
my-app-staging:
<<: *common

my-app-review:
<<: *common
match_if_app_name_starts_with: true
hooks:
post_creation: bundle exec rails db:prepare
pre_deletion: bundle exec rails db:drop

my-app-production:
<<: *common
allow_org_override_by_env: false
allow_app_override_by_env: false
cpln_org: my-org-production
upstream: my-app-staging
release_script: release_script.sh
```

Important points:

- `REVIEW_APP_PREFIX` in GitHub Actions must match the review config key prefix, for example `my-app-review`.
- `match_if_app_name_starts_with: true` is what allows a single config entry to back `my-app-review-123`, `my-app-review-456`, and cleanup commands like `cpflow cleanup-stale-apps -a my-app-review`.
- `upstream: my-app-staging` is what lets the production promotion workflow copy the exact staging artifact.
- If your main web workload is not named `rails`, set the optional `PRIMARY_WORKLOAD` repository variable described below.

## Required GitHub Repository Settings

Configure these repository secrets:

- `CPLN_TOKEN_STAGING`: token for the staging Control Plane org
- `CPLN_TOKEN_PRODUCTION`: token for the production Control Plane org

Configure these repository variables:

- `CPLN_ORG_STAGING`: staging org name, for example `company-staging`
- `CPLN_ORG_PRODUCTION`: production org name, for example `company-production`
- `STAGING_APP_NAME`: staging GVC name, for example `my-app-staging`
- `PRODUCTION_APP_NAME`: production GVC name, for example `my-app-production`
- `REVIEW_APP_PREFIX`: review-app prefix, for example `my-app-review`
- `STAGING_APP_BRANCH`: optional branch that auto-deploys staging; defaults to `main` or `master` if unset
- `PRIMARY_WORKLOAD`: optional workload name used to discover the public endpoint and do production health checks; defaults to `rails`

Recommended org layout:

- keep review apps and staging in a staging org that developers can access
- keep production in a separate org with tighter access controls

## Generated Workflow Behavior

`cpflow-review-app-help.yml`

- Posts a quick reference when a pull request opens.

`cpflow-help-command.yml`

- Replies to `/help` on a pull request with the commands and required repo settings.

`cpflow-deploy-review-app.yml`

- Creates a review app when someone comments `/deploy-review-app`.
- Redeploys an existing review app automatically on later PR pushes.
- Creates a GitHub deployment and comments with the review URL and logs.
- Leaves PR pushes alone until the first review app is explicitly requested, which keeps demo-app costs down.

`cpflow-delete-review-app.yml`

- Deletes the review app on `/delete-review-app`.
- Also deletes it automatically when the pull request closes.

`cpflow-deploy-staging.yml`

- Builds and deploys the staging app on pushes to `STAGING_APP_BRANCH`.
- Falls back to `main` or `master` when `STAGING_APP_BRANCH` is unset.

`cpflow-promote-staging-to-production.yml`

- Manually promotes the staging artifact to production with a confirmation input.
- Verifies that production has the env var names staging expects.
- Runs a health check and attempts a rollback of every container image in `PRIMARY_WORKLOAD` if the new production image does not come up healthy.
- Creates a GitHub release after a successful promotion.

`cpflow-cleanup-stale-review-apps.yml`

- Runs nightly and on demand.
- Deletes stale review apps using `cpflow cleanup-stale-apps`.

## Composite Actions

The generated workflows share these local composite actions:

- `cpflow-setup-environment`: installs Ruby, the Control Plane CLI, and the `cpflow` gem, then logs into the target org
- `cpflow-build-docker-image`: builds and pushes the app image with the desired commit SHA
- `cpflow-delete-control-plane-app`: safely deletes temporary apps and refuses to touch names outside the configured review-app prefix

## Applying This to React on Rails Demo Apps

This flow is a good fit for the React on Rails demo apps because they already follow the same basic assumptions:

- the deployable app is a Rails project
- the primary web workload is usually `rails`
- review environments should be temporary and opt-in
- staging should auto-follow a single branch
- production should promote the already-tested staging image

In practice, porting the flow into a demo app usually means:

1. Generate `.controlplane/` if the app does not have it yet.
2. Generate the `cpflow-*` GitHub Actions files.
3. Update `.controlplane/controlplane.yml` with staging, review, and production entries.
4. Add any additional app workloads the app needs at runtime, for example `sidekiq`, a Node renderer, or any other process type that should deploy the same application image.
5. Make sure the repo variables and secrets line up with those app names.
6. Adjust `PRIMARY_WORKLOAD` only if the public workload is not named `rails`.
7. Validate the real production Docker build before relying on the workflows, especially if asset compilation or SSR requires Node, extra system packages, or multiple processes.

## AI Playbook

If you want an AI agent to apply this flow to another project, this prompt is the intended starting point:

```text
Set up Control Plane GitHub Flow for this repo. If `.controlplane/` is missing, run `cpflow generate`. Then run `cpflow generate-github-actions`, update `.controlplane/controlplane.yml` so it defines `<app>-staging`, `<app>-review`, and `<app>-production`, 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/variables that must be configured. If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated workflows.
```

Expand that prompt with app-specific requirements before editing files:

- inspect the production Dockerfile and make sure it can build the app's assets in CI
- add extra `app_workloads` and template files for any runtime sidecars, workers, or renderer processes
- make sure any sidecar process exposed to sibling workloads binds to `0.0.0.0` instead of container-local `localhost`
- make sure sidecar caches or bundle directories live in writable paths for the runtime user, such as `tmp/`, instead of root-owned image paths
- keep workflow files generic and put app names, org names, and branch names in repository `vars` and `secrets`

When the agent applies this to a project, it should avoid hardcoding app names or org names into the workflow files. Those belong in repository `vars` and `secrets`.
13 changes: 13 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ Creates base Control Plane config and template files
cpflow generate
```

### `generate-github-actions`

Creates GitHub Actions templates for a Heroku Flow style Control Plane pipeline:
- on-demand review apps for pull requests
- automatic staging deploys from your main branch
- manual promotion from staging to production
- nightly cleanup and PR help workflows

```sh
# Creates .github/actions and .github/workflows files for the Control Plane flow
cpflow generate-github-actions
```

### `info`

- Displays the diff between defined/available apps/workloads (apps equal GVCs)
Expand Down
11 changes: 11 additions & 0 deletions lib/command/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@ class Generator < Thor::Group

def copy_files
directory("generator_templates", ".controlplane", verbose: ENV.fetch("HIDE_COMMAND_OUTPUT", nil) != "true")
make_shell_scripts_executable(".controlplane")
end

def self.source_root
Cpflow.root_path.join("lib")
end

private

def make_shell_scripts_executable(root_path)
Dir.glob(File.join(root_path, "**/*.sh")).each do |path|
next unless File.file?(path)

FileUtils.chmod(0o755, path)
end
end
Comment thread
justin808 marked this conversation as resolved.
end

class Generate < Base
Expand Down
Loading
Loading