-
Notifications
You must be signed in to change notification settings - Fork 374
Document cpflow workflow testing #737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| # Testing cpflow GitHub Actions Changes | ||
|
|
||
| Use this guide when changing generated `cpflow-*` GitHub Actions, updating the | ||
| `cpflow` generator version, or debugging review-app automation. | ||
|
|
||
| ## What To Test | ||
|
|
||
| Test the flow in three layers: | ||
|
|
||
| 1. Local generated-file checks catch YAML, metadata, and lint problems before a PR. | ||
| 2. GitHub workflow checks prove GitHub can load the workflow and run CI. | ||
| 3. A real review-app deploy proves the default-branch trusted actions, GitHub | ||
| secrets, Docker build, and Control Plane deploy all work together. | ||
|
|
||
| The third layer matters because the review-app workflow intentionally checks out | ||
| trusted workflow sources from the repository default branch before passing | ||
| Control Plane secrets to local composite actions. A PR branch can contain fixed | ||
| `.github/actions/*` files, but the deploy job still loads those local actions | ||
| from `master` until the fix is merged there. | ||
|
|
||
| ## Local Checks | ||
|
|
||
| After regenerating the flow, run these checks from the repository root. If | ||
| `cpflow` is installed as a gem, use `cpflow` directly: | ||
|
|
||
| ```sh | ||
| bin/conductor-exec cpflow generate-github-actions --staging-branch master | ||
| bin/test-cpflow-github-flow | ||
| ``` | ||
|
|
||
| When testing an unreleased upstream `control-plane-flow` checkout, replace | ||
| `cpflow` with that checkout's `bin/cpflow`: | ||
|
|
||
| ```sh | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one-liner is ~200 characters and will wrap unpredictably in rendered Markdown, making it easy to mis-copy (a truncated command silently does nothing or raises a syntax error). The "Ways To Make This Easier" section already suggests extracting these checks into |
||
| bin/conductor-exec ruby /path/to/control-plane-flow/bin/cpflow generate-github-actions --staging-branch master | ||
| bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow | ||
| ``` | ||
|
|
||
| Why the explicit description check exists: GitHub parses expression-like snippets | ||
| inside composite action metadata, including `description:` fields. Literal | ||
| examples such as `${{ vars.SOME_VALUE }}` can fail action loading before any | ||
| shell step starts. The wrapper runs `cpflow github-flow-readiness`, parses the | ||
| generated YAML, checks action input descriptions for literal GitHub expressions, | ||
| and runs `actionlint -ignore 'SC2129' .github/workflows/cpflow-*.yml`. | ||
|
|
||
| ## PR Checks | ||
|
|
||
| Open a normal PR for the generated-file diff and wait for CI. The workflow PR | ||
| itself is useful for syntax and CI validation, but it does not fully prove | ||
| review-app deployment changes that live under `.github/actions/`. | ||
|
|
||
| For top-level workflow edits, you can manually dispatch the PR branch workflow: | ||
|
|
||
| ```sh | ||
| gh workflow run cpflow-deploy-review-app.yml --ref <branch> -f pr_number=<pr-number> | ||
| ``` | ||
|
|
||
| This loads the workflow file from `<branch>`, but the deploy workflow's | ||
| `Checkout trusted workflow sources` step still checks out `master` before using | ||
| local composite actions with secrets. Treat this as a partial smoke test, not as | ||
| proof that PR-branch composite action changes work. | ||
|
|
||
| ## Post-Merge Review-App Test | ||
|
|
||
| After the workflow PR merges to `master`, test a real review-app deployment: | ||
|
|
||
| 1. Pick a same-repository PR to use as the canary. | ||
| 2. If the review app does not exist yet, comment exactly `+review-app-deploy` on | ||
| that PR. | ||
| 3. If a previous deploy run failed, rerun the failed deploy run after the | ||
| workflow PR is merged. | ||
| 4. Confirm the deploy run checks out `master` at the merge commit in | ||
| `Checkout trusted workflow sources`. | ||
| 5. Confirm `Setup environment` succeeds and prints the expected `cpflow` version. | ||
| 6. Confirm `Check if review app exists`, `Build Docker image`, and | ||
| `Deploy to Control Plane` all run as expected. | ||
| 7. Open the review-app URL from the PR comment or deployment status and verify | ||
| it returns HTTP 200. | ||
|
|
||
| Use the generated app name from the workflow log: | ||
|
|
||
| ```text | ||
| APP_NAME: ${REVIEW_APP_PREFIX}-${PR_NUMBER} | ||
| ``` | ||
|
|
||
| This is a template from the workflow output, not a literal command to evaluate | ||
| unless those environment variables are already set. For this repo, verify the | ||
| actual `REVIEW_APP_PREFIX` repository variable before assuming the final app | ||
| name. | ||
|
|
||
| ## Troubleshooting Signals | ||
|
|
||
| ### Composite action metadata fails before setup | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
|
||
| Error shape: | ||
|
|
||
| ```text | ||
| Unrecognized named-value: 'vars' | ||
| Failed to load ./.github/actions/cpflow-setup-environment/action.yml | ||
| ``` | ||
|
|
||
| Cause: GitHub parsed a literal expression inside composite action metadata, | ||
| usually an input description. Because trusted local actions come from `master`, | ||
| fix and merge the generated action metadata on `master`, then rerun the deploy. | ||
|
|
||
| ### Setup succeeds, then `cpflow exists` reports token format | ||
|
|
||
| Error shape: | ||
|
|
||
| ```text | ||
| ERROR: Unknown API token format. Please re-run 'cpln profile login' or set the correct CPLN_TOKEN env variable. | ||
| ``` | ||
|
|
||
| Cause: the workflow can read `CPLN_TOKEN_STAGING`, but the value is not a valid | ||
| Control Plane service-account token for the installed Control Plane CLI. Rotate | ||
| the GitHub secret, then rerun the failed deploy job. | ||
|
|
||
| ### PR pushes do not create a new review app | ||
|
|
||
| This is expected. Pushes redeploy only after the review app already exists. | ||
| Create the first review app by commenting exactly: | ||
|
|
||
| ```text | ||
| +review-app-deploy | ||
| ``` | ||
|
|
||
| ## Ways To Make This Easier | ||
|
|
||
| - Add a no-secret GitHub Actions smoke workflow that loads generated local | ||
| composite actions from the PR branch and fails fast on action metadata parsing. | ||
| - Extend `bin/test-cpflow-github-flow` as more local cpflow GitHub Actions | ||
| checks become worth standardizing. | ||
| - Add an early token sanity step after `Setup environment` so invalid | ||
| `CPLN_TOKEN_STAGING` and `CPLN_TOKEN_PRODUCTION` values fail with a named | ||
| "validate Control Plane token" step instead of surfacing later during | ||
| `cpflow exists`. | ||
| - Keep a tiny canary PR open for review-app workflow testing so post-merge | ||
| deploy verification does not depend on whichever feature PR happens to exist. | ||
| - Upstream the metadata-description check to `cpflow github-flow-readiness` so | ||
| downstream repos get the guard automatically. | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -424,24 +424,33 @@ repository variables, secrets, and docs aligned with `.controlplane/controlplane | |||||
| For this app, validate a regenerated flow with: | ||||||
|
|
||||||
| ```bash | ||||||
| bundle exec ruby /path/to/control-plane-flow/bin/cpflow generate-github-actions --staging-branch master | ||||||
| bundle exec ruby /path/to/control-plane-flow/bin/cpflow github-flow-readiness | ||||||
| actionlint .github/workflows/cpflow-*.yml | ||||||
| bundle exec rubocop | ||||||
| bin/conductor-exec ruby /path/to/control-plane-flow/bin/cpflow generate-github-actions --staging-branch master | ||||||
| bin/conductor-exec ruby /path/to/control-plane-flow/bin/cpflow github-flow-readiness | ||||||
| actionlint -ignore 'SC2129' .github/workflows/cpflow-*.yml | ||||||
| bin/conductor-exec bundle exec rubocop | ||||||
| ``` | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new
Suggested change
|
||||||
|
|
||||||
| Then open a normal PR and let GitHub Actions prove the generated review-app, | ||||||
| staging, lint, JS, and RSpec workflows before merging. For review-app workflow | ||||||
| changes, test both the local workflow syntax and a real deployment. GitHub runs | ||||||
| `issue_comment` workflows from the default branch, so a `+review-app-deploy` | ||||||
| comment on the PR does not fully exercise command changes that are only on the | ||||||
| PR branch. Before merge, run the PR branch workflow explicitly: | ||||||
| PR branch. For top-level workflow edits, run the PR branch workflow explicitly: | ||||||
|
|
||||||
| ```bash | ||||||
| gh workflow run cpflow-deploy-review-app.yml --ref <branch> -f pr_number=<pr-number> | ||||||
| ``` | ||||||
|
|
||||||
| This loads the workflow file from `<branch>`, but trusted local composite | ||||||
| actions still come from the default branch before secrets are used. Treat it as | ||||||
| a partial smoke test, then verify a real deploy after the workflow changes land | ||||||
| on `master`. | ||||||
|
|
||||||
| After the workflow reports a review-app URL, verify the URL returns HTTP 200. | ||||||
| If a project needs to track generator changes automatically, use a scheduled | ||||||
| maintenance PR or Renovate-style workflow that bumps the `cpflow` version, | ||||||
| regenerates these files, and runs the same validation commands. | ||||||
|
|
||||||
| For a fuller checklist, including the gotcha that review-app deploys load local | ||||||
| composite actions from `master` before using Control Plane secrets, see | ||||||
| [Testing cpflow GitHub Actions Changes](docs/testing-cpflow-github-actions.md). | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | ||||||||||||||||||||||||||||||||||
| cd "$ROOT" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| cpflow_cmd=(cpflow) | ||||||||||||||||||||||||||||||||||
| if [[ $# -gt 0 ]]; then | ||||||||||||||||||||||||||||||||||
| cpflow_cmd=("$@") | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When no arguments are supplied the script silently falls back to looking up A small guard here mirrors what you're already doing for
Suggested change
|
||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "==> cpflow github-flow-readiness" | ||||||||||||||||||||||||||||||||||
| bin/conductor-exec "${cpflow_cmd[@]}" github-flow-readiness | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "==> parse generated GitHub Actions YAML" | ||||||||||||||||||||||||||||||||||
| bin/conductor-exec ruby <<'RUBY' | ||||||||||||||||||||||||||||||||||
| require "yaml" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Dir[".github/actions/**/action.yml", ".github/workflows/*.yml"].sort.each do |path| | ||||||||||||||||||||||||||||||||||
| YAML.load_file(path, aliases: true) | ||||||||||||||||||||||||||||||||||
| puts "parsed #{path}" | ||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||
| RUBY | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo "==> check composite action input descriptions" | ||||||||||||||||||||||||||||||||||
| bin/conductor-exec ruby <<'RUBY' | ||||||||||||||||||||||||||||||||||
| require "yaml" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| bad = [] | ||||||||||||||||||||||||||||||||||
| Dir[".github/actions/**/action.yml"].sort.each do |path| | ||||||||||||||||||||||||||||||||||
| doc = YAML.load_file(path, aliases: true) | ||||||||||||||||||||||||||||||||||
| doc.fetch("inputs", {}).each do |name, spec| | ||||||||||||||||||||||||||||||||||
| bad << "#{path}:#{name}" if spec["description"].to_s.include?("${{") | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ruby
|
||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| abort bad.join("\n") unless bad.empty? | ||||||||||||||||||||||||||||||||||
| puts "no action metadata descriptions contain GitHub expressions" | ||||||||||||||||||||||||||||||||||
| RUBY | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If no Consider guarding with
Suggested change
Comment on lines
+38
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No check that
Suggested change
|
||||||||||||||||||||||||||||||||||
| echo "==> actionlint" | ||||||||||||||||||||||||||||||||||
| actionlint -ignore "SC2129" .github/workflows/cpflow-*.yml | ||||||||||||||||||||||||||||||||||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The preamble introduces the unreleased-checkout case, but every command in the block below uses a hard-coded path — there is no hint for readers using a normally-installed gem. Consider clarifying: