Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 111 additions & 5 deletions .controlplane/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,112 @@ In a real app, you would likely use persistent, external resources, such as AWS

You can see the definition of Postgres and Redis in the `.controlplane/templates` directory.

## GitHub Review App Setup

For normal generated review apps in this repo, GitHub needs only one repository
secret:

| Secret | Notes |
| --- | --- |
| `CPLN_TOKEN_STAGING` | Control Plane service-account token for `shakacode-open-source-examples-staging`. |

No GitHub repository variables are required for the standard review-app path.
The generated workflow infers the review app prefix
`qa-react-webpack-rails-tutorial` and staging org
`shakacode-open-source-examples-staging` from `.controlplane/controlplane.yml`
because that file defines exactly one app with
`match_if_app_name_starts_with: true`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This inference assumption is fragile: if controlplane.yml ever gains a second app entry with match_if_app_name_starts_with: true, the inference will fail (or pick the wrong one) with no clear error. Consider adding a sentence here directing readers to set CPLN_ORG_STAGING and REVIEW_APP_PREFIX explicitly if they hit ambiguous inference, so the failure mode is less opaque.


These inferred values come from `.controlplane/controlplane.yml`: the review-app
prefix is the app key with `match_if_app_name_starts_with: true`, and the
staging org is the `cpln_org` value on that app or its shared alias. The
variables below are escape hatches for forks and clones, so someone can test
this repo against their own Control Plane org, choose a different review-app
prefix, or expose a different public workload without editing the generated
workflow. Leave them unset for the standard setup.

Optional review-app override variables:

| Variable | Notes |
| --- | --- |
| `CPLN_ORG_STAGING` | Override the staging org inferred from `cpln_org` in `.controlplane/controlplane.yml`. |
| `REVIEW_APP_PREFIX` | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry. |
| `PRIMARY_WORKLOAD` | Override the public workload used to discover the review URL; leave unset for `rails`. |

For staging auto-deploys, also configure:

| Secret or variable | Value |
| --- | --- |
| `CPLN_TOKEN_STAGING` | Same staging Control Plane token used by review apps. |
| `CPLN_ORG_STAGING` | `shakacode-open-source-examples-staging` |
| `STAGING_APP_NAME` | `react-webpack-rails-tutorial-staging` |

For production promotion, configure a protected GitHub Environment named
`production`:

| Secret or variable | Value |
| --- | --- |
| `CPLN_TOKEN_PRODUCTION` | Environment secret on `production`, not a repository or organization secret. |
| `CPLN_ORG_PRODUCTION` | Environment variable on `production`: `shakacode-open-source-examples-production` |
| `PRODUCTION_APP_NAME` | Environment variable on `production`: `react-webpack-rails-tutorial-production` |

Protect the `production` environment with required reviewers, enable prevent
self-review, and consider disabling administrator bypass. Only release managers
or similarly trusted maintainers should be able to approve the promotion job.
The promotion workflow uses that environment before it can access
`CPLN_TOKEN_PRODUCTION`, so the production token is not exposed to ordinary
review-app or staging runs.

Advanced optional variables:

| Name | Notes |
| --- | --- |
| `REVIEW_APP_DEPLOYING_ICON_URL` | Cosmetic custom animated icon for review-app comments. Ignore this for the standard setup. |
| `CPLN_CLI_VERSION` | Pin only when Control Plane CLI compatibility requires it. |
| `CPFLOW_VERSION` | Runtime gem override. Leave unset when workflow wrappers are pinned to a GitHub commit SHA for upstream PR testing. |

## Control Plane Setup

The GitHub secret is only the automation credential. The Control Plane org also
needs the app resources and runtime secrets that the workloads read at boot.

For review-app testing, the standard setup is:

| Control Plane item | Where | Notes |
| --- | --- | --- |
| Staging/review org | `shakacode-open-source-examples-staging` | The `CPLN_TOKEN_STAGING` service account must be able to create and update GVCs, workloads, images, identities, policies, and secrets in this org. |
| Review app prefix | `qa-react-webpack-rails-tutorial` | Review apps are named `qa-react-webpack-rails-tutorial-<PR number>`. This is inferred from `.controlplane/controlplane.yml`. |
| Review app secret dictionary | `qa-react-webpack-rails-tutorial-secrets` | Shared by generated review apps because the QA app entry uses `match_if_app_name_starts_with: true`. |

For staging deploys later, also use:

| Control Plane item | Where | Notes |
| --- | --- | --- |
| Staging app | `react-webpack-rails-tutorial-staging` | The `CPLN_TOKEN_STAGING` token deploys this app from `master`. |
| Staging app secret dictionary | `react-webpack-rails-tutorial-staging-secrets` | Same required keys as the review app secret dictionary. |

For production promotion later, use a separate production org and token:

| Control Plane item | Where | Notes |
| --- | --- | --- |
| Production org | `shakacode-open-source-examples-production` | Do not give the staging token access to this org. |
| Production app | `react-webpack-rails-tutorial-production` | Promotion copies the staging image into this app. |
| Production app secret dictionary | `react-webpack-rails-tutorial-production-secrets` | Create before the first promotion. Use production-only values. |
| Production service-account token | GitHub Environment secret `CPLN_TOKEN_PRODUCTION` | Keep this token in the protected `production` GitHub Environment only. |

The app secret dictionaries must include:

- `SECRET_KEY_BASE`
- `RENDERER_PASSWORD`
- `REACT_ON_RAILS_PRO_LICENSE`

Generate `SECRET_KEY_BASE` with `openssl rand -hex 64` and
`RENDERER_PASSWORD` with `openssl rand -hex 32`. The review/staging template
currently contains a test placeholder for `SECRET_KEY_BASE`; replace it with a

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Documenting the placeholder is good, but this phrasing ("replace it with a secret-backed value before promoting production") leaves staging running with a known, weak SECRET_KEY_BASE. If the staging environment is internet-accessible (which review apps typically are), that's a real risk — anyone with the placeholder value can forge session cookies. Consider either removing the placeholder from the template in a follow-up and requiring an explicit secret reference, or adding a CI/readiness check that rejects deploys when SECRET_KEY_BASE matches the placeholder value.

secret-backed value before promoting production. The demo Postgres and Redis
workloads are useful for review apps and staging demos. For real production,
prefer managed services and update `DATABASE_URL` and `REDIS_URL` accordingly.

## Prerequisites

1. Ensure your [Control Plane](https://shakacode.controlplane.com) account is set up.
Expand Down Expand Up @@ -388,11 +494,11 @@ The production promotion workflow checks that production has all environment
variable names present in staging; it does not compare secret values, workload
environment variables, or Control Plane secret references.

The repository variables and secrets must match the app names in
`.controlplane/controlplane.yml`. In particular, `REVIEW_APP_PREFIX` should
include the `-pr` suffix for this app, such as
`qa-react-webpack-rails-tutorial-pr`, so generated review apps are named
`qa-react-webpack-rails-tutorial-pr-1234`.
The GitHub settings and Control Plane resources must match the app names in
`.controlplane/controlplane.yml`. For the standard review-app path, leave
`REVIEW_APP_PREFIX` unset and let the workflow infer

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This silently changes the naming convention from the previously documented qa-react-webpack-rails-tutorial-pr-<N> (with the -pr suffix) to qa-react-webpack-rails-tutorial-<N>. Any review apps currently running under the old names, bookmark links, or CI output referencing them will break without warning.

Consider adding a migration note here, even one sentence: "If you previously had REVIEW_APP_PREFIX set to qa-react-webpack-rails-tutorial-pr, delete existing review apps before switching — the old names will become orphaned resources in Control Plane."

`qa-react-webpack-rails-tutorial`; generated review apps are named
`qa-react-webpack-rails-tutorial-<PR number>`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

This allows teams to:
- Preview changes in a production-like environment
Expand Down
34 changes: 23 additions & 11 deletions .controlplane/shakacode-team.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ Deployments are handled by Control Plane configuration in this repo and GitHub A

### Review Apps
- Add a comment `+review-app-deploy` to any PR to deploy a review app
- The generated app name is `${REVIEW_APP_PREFIX}-${PR_NUMBER}`. Keep
`REVIEW_APP_PREFIX` set to `qa-react-webpack-rails-tutorial-pr` so review
apps use names like `qa-react-webpack-rails-tutorial-pr-1234`, matching the
prefix-backed config in `.controlplane/controlplane.yml`.
- Leave `REVIEW_APP_PREFIX` unset for the standard path. The workflow infers
`qa-react-webpack-rails-tutorial` from `.controlplane/controlplane.yml`, so
generated review apps use names like
`qa-react-webpack-rails-tutorial-1234`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Breaking change — naming convention. The old scheme was qa-react-webpack-rails-tutorial-pr-<N>; the new scheme omits the -pr segment. Any live review apps created under the old convention become orphaned after this merges: the cleanup and delete workflows query by the new prefix and won't find them. Recommend adding a migration note (e.g. in the PR description or a one-time manual cleanup step) so operators know to delete old-style apps before merging.

- New pushes to a PR redeploy only after the review app already exists.
- Add `+review-app-delete` to delete a review app manually; closing the PR also
deletes it automatically. Use `+review-app-help` for the command reference.
Expand All @@ -30,21 +30,33 @@ Deployments are handled by Control Plane configuration in this repo and GitHub A

### GitHub Repository Settings

Required repository secrets:
Required repository secret for review apps and staging:

- `CPLN_TOKEN_STAGING`
- `CPLN_TOKEN_PRODUCTION`

Required repository variables:
Required repository variables for staging deploys:

- `CPLN_ORG_STAGING=shakacode-open-source-examples-staging`
- `CPLN_ORG_PRODUCTION=shakacode-open-source-examples-production`
- `STAGING_APP_NAME=react-webpack-rails-tutorial-staging`
- `PRODUCTION_APP_NAME=react-webpack-rails-tutorial-production`
- `REVIEW_APP_PREFIX=qa-react-webpack-rails-tutorial-pr`
- `STAGING_APP_BRANCH=master`
- `PRIMARY_WORKLOAD=rails`

Review apps infer `CPLN_ORG_STAGING`, `REVIEW_APP_PREFIX`, and
`PRIMARY_WORKLOAD` from `.controlplane/controlplane.yml` and workflow defaults,
so those values do not need to be set just to test review apps. Set them only
when testing a fork or clone against a different Control Plane org, review-app
prefix, or public workload.

Production promotion uses a protected GitHub Environment named `production`:

- Environment secret `CPLN_TOKEN_PRODUCTION`
- Environment variable `CPLN_ORG_PRODUCTION=shakacode-open-source-examples-production`
- Environment variable `PRODUCTION_APP_NAME=react-webpack-rails-tutorial-production`

Protect the `production` environment with required reviewers, enable prevent
self-review, and consider disabling administrator bypass. Do not store
`CPLN_TOKEN_PRODUCTION` as a repository or organization secret.

Optional repository settings:

- `DOCKER_BUILD_SSH_KEY`: secret for private SSH dependencies during Docker builds.
Expand All @@ -66,7 +78,7 @@ filter in `.github/workflows/cpflow-deploy-staging.yml`.
When the upstream `control-plane-flow` repo changes the generated GitHub Actions
flow, regenerate the `cpflow-*` actions/workflows in this repo from the target
`cpflow` version or branch using `--staging-branch master`, review the diff, and
keep the repository variables above aligned with `.controlplane/controlplane.yml`. Validate with
keep the GitHub settings above aligned with `.controlplane/controlplane.yml`. Validate with
`cpflow github-flow-readiness`, `actionlint .github/workflows/cpflow-*.yml`, and
the normal CI checks before merging. For review-app workflow changes, remember
that the deploy workflow checks out trusted local actions from `master` before
Expand Down
74 changes: 66 additions & 8 deletions .github/cpflow-help.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,84 @@ You asked for review app help. These commands are generated by [cpflow](https://
| Name | Required | Notes |
| --- | --- | --- |
| `CPLN_TOKEN_STAGING` | yes | Service-account token scoped to the staging Control Plane org on controlplane.com. |
| `CPLN_TOKEN_PRODUCTION` | yes (for promote) | Service-account token scoped to the production Control Plane org on controlplane.com. |
| `CPLN_TOKEN_PRODUCTION` | yes (for promote) | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. |
| `DOCKER_BUILD_SSH_KEY` | optional | Private SSH key used when Docker builds fetch private deps via `RUN --mount=type=ssh`. |

For normal generated review apps, `CPLN_TOKEN_STAGING` is the only required
GitHub setting. The review app prefix and staging org are inferred from
`.controlplane/controlplane.yml` when it defines exactly one app with
`match_if_app_name_starts_with: true`.

Those inferred values come from `.controlplane/controlplane.yml`: `cpln_org`
selects the Control Plane org and the app key with
`match_if_app_name_starts_with: true` becomes the review-app prefix. The
optional review-app variables below are override hooks for forks and clones that
want to test against their own Control Plane org, use a different prefix, or
expose a different public workload. Leave them unset for the standard setup.

For production promotion, create a GitHub Environment named `production`, add
required reviewers, enable prevent self-review, and store
`CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated promotion
workflow uses that environment before it can access production secrets.

### GitHub Actions variables

| Name | Required | Notes |
| --- | --- | --- |
| `CPLN_ORG_STAGING` | yes | Control Plane org on controlplane.com for staging and review apps. |
| `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. |
| `CPLN_ORG_STAGING` | optional for review apps; yes for staging | Override the staging/review Control Plane org inferred from `controlplane.yml`. |
| `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. |

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 Required column says yes (for promote) — which implies a repository variable — but the Notes column says "Prefer a production environment variable." These two signals contradict each other. Readers who scan the Required column will put this in repo variables; readers who follow the note will put it in the protected environment. Suggest picking one canonical placement and stating it clearly, e.g. updating the Required cell to yes — environment variable or splitting the secrets and environment-variable rows into separate tables.

| `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. |
| `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. |
| `REVIEW_APP_PREFIX` | yes | Prefix for per-PR review app names (e.g. `review-app`). |
| `REVIEW_APP_DEPLOYING_ICON_URL` | optional | Custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. |
| `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same issue as CPLN_ORG_PRODUCTION above: yes (for promote) implies a repo variable, but the Notes column recommends a production environment variable. Both entries need a consistent placement signal.

| `REVIEW_APP_PREFIX` | optional | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry in `controlplane.yml`. |
| `REVIEW_APP_DEPLOYING_ICON_URL` | optional, advanced | Cosmetic custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. |
| `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. |
| `PRIMARY_WORKLOAD` | optional | Workload polled for health and rollback (defaults to `rails`). |
| `PRIMARY_WORKLOAD` | optional | Override the public workload used for the review URL, health checks, and rollback (defaults to `rails`). |
| `DOCKER_BUILD_EXTRA_ARGS` | optional | Newline-delimited extra docker build tokens (e.g. `--build-arg=FOO=bar`). |
| `DOCKER_BUILD_SSH_KNOWN_HOSTS` | optional | SSH known_hosts entries when SSH build hosts are not GitHub.com. |
| `HEALTH_CHECK_ACCEPTED_STATUSES` | optional | Space-separated HTTP statuses considered healthy on promote (default `200 301 302`). |
| `CPLN_CLI_VERSION` | optional | Pin a specific `@controlplane/cli` version; falls back to the action default when unset. |
| `CPFLOW_VERSION` | optional | Runtime gem-install override. When unset, cpflow is built from the pinned upstream workflow ref. When set, use the RubyGems version without a leading `v`. |
| `CPFLOW_VERSION` | optional | Pin a published RubyGems version such as `5.0.0` or `5.0.0.rc.1`. Leave unset for normal generated workflows so the setup action builds `cpflow` from the same `control-plane-flow` GitHub ref used by the reusable workflow. |

</details>

<details>
<summary>Advanced: testing unreleased control-plane-flow changes</summary>

Generated workflow wrappers have two related pins:

- The `uses: shakacode/control-plane-flow/...@<ref>` GitHub ref selects the reusable workflow code.
- The `control_plane_flow_ref: <ref>` input tells the setup action which `control-plane-flow` source to check out and build when `CPFLOW_VERSION` is empty.

The GitHub ref is the runtime lock for workflow and action behavior. The
RubyGems version is used to generate/update these wrappers and, only when
`CPFLOW_VERSION` is set, to install the `cpflow` CLI at runtime. A downstream
repo cannot rely on the gem alone because GitHub loads reusable workflow YAML
from `shakacode/control-plane-flow`, not from RubyGems.

For normal releases, point both pins at a release tag such as `v5.0.0`.
You may leave `CPFLOW_VERSION` unset, or set it to the matching RubyGems version
without the leading `v`, such as `5.0.0`. If you set `CPFLOW_VERSION` for a
prerelease, use RubyGems dot syntax such as `5.0.0.rc.1`; the release tag may
use either `v5.0.0.rc.1` or `v5.0.0-rc.1`.

To update to a new stable release, install or bundle the new `cpflow` gem, run
`cpflow generate-github-actions`, and commit the regenerated wrappers. Use
`bin/pin-cpflow-github-ref vX.Y.Z` only when the generated files are already
current and you only need to move the upstream GitHub ref.

For temporary downstream testing of an upstream PR before a gem is released, pin
both values to the exact 40-character commit SHA and leave `CPFLOW_VERSION`
unset:

```sh
bin/pin-cpflow-github-ref <40-character-control-plane-flow-commit-sha>
bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow
```

Do not leave downstream apps pinned to a moving branch such as `main`. A branch
can pick up beta or work-in-progress changes without a downstream PR changing.
Use commit SHAs for short-lived PR testing, then switch to the release tag once
the gem and tag exist.

</details>

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cpflow-cleanup-stale-review-apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:

jobs:
cleanup:
uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@db013e139af4ee8741f791c14ff825f13c0a1021
with:
control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021
secrets: inherit
4 changes: 2 additions & 2 deletions .github/workflows/cpflow-delete-review-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
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@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@db013e139af4ee8741f791c14ff825f13c0a1021
with:
control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021
secrets: inherit
4 changes: 2 additions & 2 deletions .github/workflows/cpflow-deploy-review-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
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@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@db013e139af4ee8741f791c14ff825f13c0a1021
with:
control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021
secrets: inherit
4 changes: 2 additions & 2 deletions .github/workflows/cpflow-deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ permissions:

jobs:
deploy-staging:
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@db013e139af4ee8741f791c14ff825f13c0a1021
with:
control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021
staging_app_branch_default: ""
secrets: inherit
2 changes: 1 addition & 1 deletion .github/workflows/cpflow-help-command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ jobs:
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@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4
uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@db013e139af4ee8741f791c14ff825f13c0a1021
Loading
Loading