Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 7 additions & 19 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
# Contributing to cascade

Thanks for your interest in contributing. This document covers what you need to
get set up and land a change.
Thanks for your interest in contributing. This document covers what you need to get set up and land a change.

## Developer Certificate of Origin

cascade uses the [Developer Certificate of Origin](https://developercertificate.org/).
Every commit must be signed off, which certifies you wrote the change or have the
right to submit it under the project's license:
cascade uses the [Developer Certificate of Origin](https://developercertificate.org/). Every commit must be signed off, which certifies you wrote the change or have the right to submit it under the project's license:

```bash
git commit -s -m "your message"
```

The sign-off adds a `Signed-off-by: Your Name <you@example.com>` line using your
`git config user.name` and `user.email`. Pull requests with unsigned commits will
be asked to amend.
The sign-off adds a `Signed-off-by: Your Name <you@example.com>` line using your `git config user.name` and `user.email`. Pull requests with unsigned commits will be asked to amend.

## Development setup

Expand All @@ -40,21 +35,14 @@ golangci-lint run ./...
1. Open an issue first for anything non-trivial so we can agree on the approach.
2. Branch from `main`.
3. Keep changes focused. One logical change per pull request.
4. Add or update tests. New manifest fields and generator features need an `e2e/`
scenario, not just a unit test on generated output.
4. Add or update tests. New manifest fields and generator features need an `e2e/` scenario, not just a unit test on generated output.
5. Run `go test ./...` and `golangci-lint run ./...` before pushing.
6. Use [Conventional Commits](https://www.conventionalcommits.org/) for commit
messages (`feat:`, `fix:`, `docs:`, `chore:`, ...). cascade derives changelogs
and version bumps from them.
6. Use [Conventional Commits](https://www.conventionalcommits.org/) for commit messages (`feat:`, `fix:`, `docs:`, `chore:`, ...). cascade derives changelogs and version bumps from them.

## API design

Public APIs follow a functional-options style: required inputs are positional and
optional or extensible behavior arrives as a variadic `...Option` tail, so new
capability is additive and never a breaking signature change. Cross-cutting
concerns are small interfaces with no-op defaults rather than forced dependencies.
Public APIs follow a functional-options style: required inputs are positional and optional or extensible behavior arrives as a variadic `...Option` tail, so new capability is additive and never a breaking signature change. Cross-cutting concerns are small interfaces with no-op defaults rather than forced dependencies.

## Reporting bugs

Open an issue with the manifest config, the generated workflow (if relevant), and
what you expected versus what happened. A minimal reproduction helps a lot.
Open an issue with the manifest config, the generated workflow (if relevant), and what you expected versus what happened. A minimal reproduction helps a lot.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@
<p align="center"><strong>Declarative trunk-based CI/CD for GitHub Actions.</strong></p>

<p align="center">
Define what to build and where to deploy in one manifest.<br>
cascade generates the GitHub Actions wiring, tracks deployment state, manages releases,<br>
and cascades promotions through your environments.
Define what to build and where to deploy in one manifest.<br> cascade generates the GitHub Actions wiring, tracks deployment state, manages releases,<br> and cascades promotions through your environments.
</p>

---
Expand Down
36 changes: 9 additions & 27 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,30 @@
| Latest release line | Yes, security fixes shipped promptly |
| Older releases | No |

The latest release line is the active one. Only the most recent release receives
security patches. Upgrade to the latest release to stay covered.
The latest release line is the active one. Only the most recent release receives security patches. Upgrade to the latest release to stay covered.

The schema-version compatibility policy (which CLI versions read which manifest
versions) is documented separately in
[versioning and schema compatibility](https://stablekernel.github.io/cascade/versioning/).
The schema-version compatibility policy (which CLI versions read which manifest versions) is documented separately in [versioning and schema compatibility](https://stablekernel.github.io/cascade/versioning/).

## Reporting a vulnerability

Please do **not** open a public GitHub issue for security vulnerabilities.

Report them privately via
[GitHub Security Advisories](https://github.com/stablekernel/cascade/security/advisories/new)
using the "Report a vulnerability" button on the repository's Security tab. This
needs no email and keeps the report private until a fix is coordinated.
Report them privately via [GitHub Security Advisories](https://github.com/stablekernel/cascade/security/advisories/new) using the "Report a vulnerability" button on the repository's Security tab. This needs no email and keeps the report private until a fix is coordinated.

<!-- Maintainers: enable Private Vulnerability Reporting under Settings > Code
security so the "Report a vulnerability" button is available. -->
Include a description of the issue, steps to reproduce, and any relevant version
information.
Include a description of the issue, steps to reproduce, and any relevant version information.

**Response expectations**

- You will receive an acknowledgement within 3 business days.
- We aim to triage and confirm the issue within 7 days.
- A fix or mitigation will be released as soon as practicable, typically within
30 days for high-severity findings.
- A fix or mitigation will be released as soon as practicable, typically within 30 days for high-severity findings.

We follow
[coordinated disclosure](https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure):
please allow us reasonable time to address the issue before making it public.
We follow [coordinated disclosure](https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure): please allow us reasonable time to address the issue before making it public.

## Security model

Cascade is a build-time tool that generates GitHub Actions workflows you commit
and review in your own repository. The generated workflows run under your own
runners, branch protection, and environment gates, and cross-repo coordination
uses a same-organization, shared-token model where a dispatch token you provision
is the trust boundary. Deploying cascade safely is therefore a shared
responsibility between cascade and your organization's GitHub and cloud
configuration.

See the
[security and hardening guide](https://stablekernel.github.io/cascade/security/hardening/)
for the full model and a step-by-step hardening checklist.
Cascade is a build-time tool that generates GitHub Actions workflows you commit and review in your own repository. The generated workflows run under your own runners, branch protection, and environment gates, and cross-repo coordination uses a same-organization, shared-token model where a dispatch token you provision is the trust boundary. Deploying cascade safely is therefore a shared responsibility between cascade and your organization's GitHub and cloud configuration.

See the [security and hardening guide](https://stablekernel.github.io/cascade/security/hardening/) for the full model and a step-by-step hardening checklist.
10 changes: 2 additions & 8 deletions docs/src/content/docs/adoption.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,14 @@ The flow in one line: you write a manifest plus callback workflows, run `cascade

## Build a pipeline from scratch

**Fast path:** `cascade init` does the first three steps below for you. It
scaffolds the manifest and the callback stubs, verifies them through the real
generator, and writes them into your repository:
**Fast path:** `cascade init` does the first three steps below for you. It scaffolds the manifest and the callback stubs, verifies them through the real generator, and writes them into your repository:

```bash
cascade init --topology two-env # dev, prod
cascade init --envs staging,production # your own ordered names
```

Pick a preset with `--topology` (`no-env`, `two-env`, `three-env`, `four-env`)
or supply your own ordered list with `--envs`. Then jump to step 4 to generate
and commit. The walkthrough below explains each piece `init` produces, so you
understand what you are filling in. See the [CLI Reference](/cascade/cli-reference/#init)
for every flag.
Pick a preset with `--topology` (`no-env`, `two-env`, `three-env`, `four-env`) or supply your own ordered list with `--envs`. Then jump to step 4 to generate and commit. The walkthrough below explains each piece `init` produces, so you understand what you are filling in. See the [CLI Reference](/cascade/cli-reference/#init) for every flag.

### 1. Choose your environments

Expand Down
54 changes: 10 additions & 44 deletions docs/src/content/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,43 +564,25 @@ For satellite repos with notify config:

### What cascade does today

The generator emits an `environment: <name>` key on each deploy job whenever the
manifest includes an `environments` list. That single key is enough for GitHub
Actions to attach deployment records, honour required-reviewer gates, apply
wait timers, and scope environment secrets. You configure all of that inside
GitHub, not in the manifest. No cascade code calls the Deployments REST API or
the Environments REST API directly.
The generator emits an `environment: <name>` key on each deploy job whenever the manifest includes an `environments` list. That single key is enough for GitHub Actions to attach deployment records, honour required-reviewer gates, apply wait timers, and scope environment secrets. You configure all of that inside GitHub, not in the manifest. No cascade code calls the Deployments REST API or the Environments REST API directly.

### What is deferred

Two capabilities are intentionally out of scope for v1:

- Programmatic Deployments API status. cascade does not call
`POST /repos/{owner}/{repo}/deployments` or
`POST /repos/{owner}/{repo}/deployments/{id}/statuses`. GitHub Actions creates
these records automatically when a job carries `environment:`, so adopters get
deployment records without cascade owning that call.
- Programmatic Deployments API status. cascade does not call `POST /repos/{owner}/{repo}/deployments` or `POST /repos/{owner}/{repo}/deployments/{id}/statuses`. GitHub Actions creates these records automatically when a job carries `environment:`, so adopters get deployment records without cascade owning that call.

- Environments REST configuration sync. cascade does not read or write
environment protection rules (required reviewers, wait timers, branch policies)
via the REST API. That configuration lives in GitHub today.
- Environments REST configuration sync. cascade does not read or write environment protection rules (required reviewers, wait timers, branch policies) via the REST API. That configuration lives in GitHub today.

### Why deferred

Keeping cascade out of these APIs in v1 bounds the surface area and avoids
coupling the tool to GitHub API semantics that are still evolving. The
auto-created deployment records from `environment:` already cover the common
case. Adding programmatic control before an adopter needs it would buy
complexity and nothing else. If those APIs change shape, cascade would have to
track the change even though nothing in v1 depends on them.
Keeping cascade out of these APIs in v1 bounds the surface area and avoids coupling the tool to GitHub API semantics that are still evolving. The auto-created deployment records from `environment:` already cover the common case. Adding programmatic control before an adopter needs it would buy complexity and nothing else. If those APIs change shape, cascade would have to track the change even though nothing in v1 depends on them.

### How the design reserves the extension points

The schema already carries the hooks needed to add both capabilities later
without a breaking change:
The schema already carries the hooks needed to add both capabilities later without a breaking change:

**`environment_config` reserved shape.** The manifest schema reserves an
`environment_config` block at the `config:` level, keyed by environment name:
**`environment_config` reserved shape.** The manifest schema reserves an `environment_config` block at the `config:` level, keyed by environment name:

```yaml
config:
Expand All @@ -614,31 +596,15 @@ config:
# branch_policy: protected
```

The `environments` list stays a plain ordered `[]string`; the separate
`environment_config` map carries per-env settings. Adding fields under
`environment_config.<name>` is additive and never touches the ordering semantics
of `environments`. A manifest that omits `environment_config` entirely is valid
and equivalent to today's behaviour.
The `environments` list stays a plain ordered `[]string`; the separate `environment_config` map carries per-env settings. Adding fields under `environment_config.<name>` is additive and never touches the ordering semantics of `environments`. A manifest that omits `environment_config` entirely is valid and equivalent to today's behaviour.

**Single finalize seam.** The `orchestrate.Finalize` and `promote.Finalize`
functions are the only places that write state after a deployment completes.
A future Deployments API call attaches at one of those two points, not scattered
across the generator. That code constraint is already in place.
**Single finalize seam.** The `orchestrate.Finalize` and `promote.Finalize` functions are the only places that write state after a deployment completes. A future Deployments API call attaches at one of those two points, not scattered across the generator. That code constraint is already in place.

**Generator delegates environment semantics to GitHub.** Because the generator
emits `environment:` and nothing more, it does not embed logic about what that
environment means. Programmatic status reporting slots in at finalize time;
Environments REST configuration sync is a separate operational concern that never
needs to touch the generator.
**Generator delegates environment semantics to GitHub.** Because the generator emits `environment:` and nothing more, it does not embed logic about what that environment means. Programmatic status reporting slots in at finalize time; Environments REST configuration sync is a separate operational concern that never needs to touch the generator.

### Forward-compatibility guarantee

Both capabilities, when they arrive, will follow the same additive-only policy
described in [Versioning & Schema](/cascade/versioning/): new optional fields under
`environment_config.<name>`, new optional top-level blocks if needed, and no
removal or re-typing of existing fields. Neither will require a `schema_version`
bump. Manifests that do not opt in to the new fields continue to work exactly as
they do today.
Both capabilities, when they arrive, will follow the same additive-only policy described in [Versioning & Schema](/cascade/versioning/): new optional fields under `environment_config.<name>`, new optional top-level blocks if needed, and no removal or re-typing of existing fields. Neither will require a `schema_version` bump. Manifests that do not opt in to the new fields continue to work exactly as they do today.

## Testing Strategy

Expand Down
16 changes: 3 additions & 13 deletions docs/src/content/docs/callback-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,14 +479,9 @@ ci:

## Environment Protection

Use GitHub Environment protection for approval gates. Because every deploy is a
reusable workflow, declare the `environment:` key on the job **inside your
reusable workflow**. GitHub Actions only allows a job-level `environment:` key on
a steps job, never on a job that calls a reusable workflow with `uses:`, so the
caller job cascade generates cannot carry it.
Use GitHub Environment protection for approval gates. Because every deploy is a reusable workflow, declare the `environment:` key on the job **inside your reusable workflow**. GitHub Actions only allows a job-level `environment:` key on a steps job, never on a job that calls a reusable workflow with `uses:`, so the caller job cascade generates cannot carry it.

cascade passes the target environment name to your workflow as the `environment`
input, so wire it through:
cascade passes the target environment name to your workflow as the `environment` input, so wire it through:

```yaml
# your reusable deploy workflow
Expand All @@ -498,12 +493,7 @@ jobs:
- run: ./deploy.sh
```

cascade cannot set `environment:` on the caller job it generates: GitHub Actions
rejects a workflow that puts `environment:` on a `uses:` job. cascade therefore
emits only the `with: environment:` input on the caller and relies on your
reusable workflow to apply the protection rules. cascade prints a generate-time
note when `gha_environment` is configured for an environment, reminding you to
declare `environment:` inside the reusable workflow.
cascade cannot set `environment:` on the caller job it generates: GitHub Actions rejects a workflow that puts `environment:` on a `uses:` job. cascade therefore emits only the `with: environment:` input on the caller and relies on your reusable workflow to apply the protection rules. cascade prints a generate-time note when `gha_environment` is configured for an environment, reminding you to declare `environment:` inside the reusable workflow.

Configure protection in GitHub: **Settings -> Environments -> Add required reviewers**.

Expand Down
9 changes: 1 addition & 8 deletions docs/src/content/docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,7 @@ cascade promote preflight \
| `--rollback-on-failure` | bool | true | Revert successful deploys if any fails |
| `--allow-downgrade` | bool | false | Permit promoting an older version onto an env (a downgrade). Blocked by default; prod always requires this flag |

A promotion that would place a strictly older semver version onto an env than the
version it currently holds is a downgrade. Preflight blocks it by default, naming
both versions and the env. Pass `--allow-downgrade` to permit it. The terminal
(prod) env always requires the flag, even when a lower env in the same cascade
already permitted the same downgrade. Equal versions are an idempotent
re-promote and are never treated as a downgrade. When either version is not
parseable as semver the gate fails open with a warning rather than blocking, so
non-semver pipelines keep working.
A promotion that would place a strictly older semver version onto an env than the version it currently holds is a downgrade. Preflight blocks it by default, naming both versions and the env. Pass `--allow-downgrade` to permit it. The terminal (prod) env always requires the flag, even when a lower env in the same cascade already permitted the same downgrade. Equal versions are an idempotent re-promote and are never treated as a downgrade. When either version is not parseable as semver the gate fails open with a warning rather than blocking, so non-semver pipelines keep working.

##### Output

Expand Down
Loading
Loading