Skip to main content

v2.9.0

Release notes for orun v2.9.0.

Highlights

  • Dependency include policy (dependsOn[].include). Dependencies now separate two orthogonal questions: ordering (existing dependsOn / dependencyMode behavior) and plan inclusion. The new include field controls whether a dependency should be pulled into a --changed plan when only the dependent component changed.
  • Behavior change for --changed planning. The default is now include: if-selected, which means change-detected plans only contain components that actually changed (plus their order edges when both ends are already in the plan). Components that were silently pulled in by transitive dependsOn in v2.8.0 and earlier are no longer included unless authors explicitly opt in with include: always.

New: dependsOn[].include

ValueSelection behaviorBest for
if-selectedAdd an ordering edge only when both components are already selectedDefault for most component dependencies
alwaysPull the dependency into the plan and add the ordering edgeMigrations, codegen, shared infra, parent package build

if-selected is the built-in default and is applied during normalization.

Default: order-only

metadata:
name: web-console
spec:
type: cloudflare-pages-turbo
dependsOn:
- component: ui-package
# include: if-selected (default)

Change-detection behavior:

changed: web-console            -> plan: web-console
changed: ui-package -> plan: ui-package
changed: web-console + ui-pkg -> plan: ui-package -> web-console
full plan -> plan: ui-package -> web-console

Opt-in: always pull the dependency in

metadata:
name: api-edge-worker
spec:
type: cloudflare-worker-turbo
dependsOn:
- component: identity-schema
include: always
reason: api requires latest generated identity contract before deploy
changed: api-edge-worker  -> plan: identity-schema -> api-edge-worker

Use include: always for schema generation, database migrations, codegen, artifact publishing, or any dependency whose output must be refreshed before the dependent runs.

Planner algorithm

The planner now thinks in three sets:

ChangedSet      = components directly changed (files / trigger scope)
SelectedSet = components included in this plan
DependencyEdges = ordering edges between selected jobs
  1. Seed SelectedSet from changed / full / explicit scope.
  2. Walk SelectedSet along each component's dependsOn.
  3. For each edge:
    • include: if-selected — do NOT add the dependency; add the edge only if the dependency is already selected.
    • include: always — add the dependency to SelectedSet, then add the edge.
  4. Repeat until SelectedSet stops growing.
  5. Compile, validate the DAG, render the plan.

This keeps the planner deterministic and keeps the compiled plan as the visible truth — the runner consumes the plan rather than reinterpreting intent.

Orthogonal semantics

dependsOn       = ordering relationship
include = plan selection behavior (new in v2.9.0)
condition = success/failure gating behavior
dependencyMode = enforced / advisory / disabled (v2.8.0)

These can be combined. For example, a PR plan can use dependencyMode: advisory (parallel feedback) together with the default include: if-selected (don't drag unchanged components in).

Migration

If a v2.8.0 intent file relied on --changed plans transitively pulling in dependencies, add include: always to those edges:

 dependsOn:
- component: database-migration
+ include: always
+ reason: api cannot deploy without fresh schema

The CWD context scope (orun plan from inside a component directory) intentionally keeps the old "give me this component and everything it needs" behavior so you still see the full footprint of a fresh plan when working locally on a single component.

Validation

Invalid include values are rejected at plan time:

component api: dependsOn[0].include "sometimes" is invalid
(expected "if-selected" or "always")

Missing-dependency errors are now scoped to include: always:

dependency not found: api.pr depends on db.pr (include: always)

Previously a missing dep was always an error. Under the new default this is just an order-only edge that drops silently when the other end isn't part of the plan — which is exactly what --changed wants.