Skip to content

Support nested and chained ternaries in recipe parameter resolver#12239

Merged
willdavsmith merged 1 commit into
mainfrom
willdavsmith-willdavsmith-recipe-ternary-resolver
Jun 25, 2026
Merged

Support nested and chained ternaries in recipe parameter resolver#12239
willdavsmith merged 1 commit into
mainfrom
willdavsmith-willdavsmith-recipe-ternary-resolver

Conversation

@willdavsmith

Copy link
Copy Markdown
Contributor

Description

Fixes the direct-module recipe parameter resolver (pkg/recipes/paramresolver/resolver.go) so that chained and nested ternary expressions resolve correctly.

Problem

The resolver evaluated only a single flat == ternary with string-literal arms using one regex. A chained enum mapping like:

size == "S" ? "Basic" : size == "M" ? "Standard" : "Premium"

mis-bound the regex capture groups, so the literal {{...}} expression was passed through verbatim to the IaC engine (Bicep/Terraform) instead of being resolved to the intended value.

Approach (smallest-diff slice)

Only the ternary evaluator is made recursive — nothing else changes:

  • Replace the single-level ternaryPattern regex with a condition-only conditionPattern (<context path> == "value").
  • Rewrite evaluateTernary as a thin wrapper over a recursive core: evalTernaryExpr, evalTernaryArm, and splitTopLevelTernary.
  • splitTopLevelTernary locates the outermost ?/: while skipping quoted string literals and tracking nested ternary depth, so chained/nested arms split and resolve at the correct level.

The public ResolveParameterExpressions signature is unchanged, and the call sites in pkg/recipes/driver/bicep/bicep.go and pkg/recipes/terraform/execute.go are untouched.

Limitations kept by design

These remain as documented limitations (tracked separately on the issue):

  • The only supported operator is ==.
  • Arms must be string literals or nested ternaries (no context paths or typed results in arms).
  • Expressions whose chosen branch cannot be resolved still pass through verbatim rather than failing.

Test coverage

Test_TernaryExpressions now covers 11 cases (5 new), including chained first/middle/default-branch matches, a nested ternary in the true arm, unresolvable-chosen-branch passthrough, and a clarified brace-wrapped passthrough case. All 11 subtests pass; paramresolver, driver/bicep, and driver/terraform packages all pass; go build ./..., go vet, and gofmt are clean.

Addresses #12238

The direct-module recipe parameter resolver previously evaluated only a
single flat `==` ternary with string-literal arms via one regex. Chained
or nested enum mappings such as
`size == "S" ? "Basic" : size == "M" ? "Standard" : "Premium"` mis-bound
the regex capture groups, so the literal `{{...}}` expression was passed
through verbatim to the IaC engine instead of being resolved.

Replace the single-level `ternaryPattern` regex with a condition-only
`conditionPattern` and rewrite `evaluateTernary` as a thin wrapper over a
recursive core (`evalTernaryExpr`, `evalTernaryArm`, `splitTopLevelTernary`).
`splitTopLevelTernary` scans for the outermost `?`/`:` outside quoted string
literals while tracking nested ternary depth, so chained and nested arms
split and resolve correctly.

The public `ResolveParameterExpressions` signature and the bicep/terraform
call sites are unchanged. Documented limitations are intentionally kept:
the only operator is `==`, arms must be string literals or nested ternaries,
and expressions whose chosen branch cannot be resolved still pass through
unchanged rather than failing.

Addresses #12238

Signed-off-by: willdavsmith <willdavsmith@gmail.com>
@willdavsmith willdavsmith requested a review from a team as a code owner June 24, 2026 20:45
Copilot AI review requested due to automatic review settings June 24, 2026 20:45
@willdavsmith willdavsmith requested a review from a team as a code owner June 24, 2026 20:45
@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@radius-functional-tests

radius-functional-tests Bot commented Jun 24, 2026

Copy link
Copy Markdown

Radius functional test overview

🔍 Go to test action run

Click here to see the test run details
Name Value
Repository radius-project/radius
Commit ref ce2fd90
Unique ID funcab760c1f7b
Image tag pr-funcab760c1f7b
  • KinD: v0.29.0
  • Dapr: 1.14.4
  • Azure KeyVault CSI driver: 1.4.2
  • Azure Workload identity webhook: 1.3.0
  • Bicep recipe location ghcr.io/radius-project/dev/test/testrecipes/test-bicep-recipes/<name>:pr-funcab760c1f7b
  • Terraform recipe location http://tf-module-server.radius-test-tf-module-server.svc.cluster.local/<name>.zip (in cluster)
  • applications-rp test image location: ghcr.io/radius-project/dev/applications-rp:pr-funcab760c1f7b
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:pr-funcab760c1f7b
  • controller test image location: ghcr.io/radius-project/dev/controller:pr-funcab760c1f7b
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:pr-funcab760c1f7b
  • deployment-engine test image location: ghcr.io/radius-project/deployment-engine:latest

Test Status

⌛ Building Radius and pushing container images for functional tests...
✅ Container images build succeeded
⌛ Publishing Bicep Recipes for functional tests...
✅ Recipe publishing succeeded
⌛ Starting ucp-cloud functional tests...
⌛ Starting corerp-cloud functional tests...
✅ ucp-cloud functional tests succeeded
✅ corerp-cloud functional tests succeeded

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the direct-module recipe parameter resolver’s ternary evaluation logic so chained and nested ternary expressions inside {{ ... }} are parsed and resolved correctly, addressing the regex mis-binding issue described in #12238.

Changes:

  • Replaces the single-level ternary regex with a condition-only regex and introduces a recursive ternary evaluator.
  • Adds a scanner (splitTopLevelTernary) to split at the outermost ? / matching : while skipping quoted strings and tracking nesting depth.
  • Expands unit tests to cover chained and nested ternary scenarios, plus passthrough cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pkg/recipes/paramresolver/resolver.go Reworks ternary parsing/evaluation to support chained/nested ternaries via recursion and a top-level split scanner.
pkg/recipes/paramresolver/resolver_test.go Adds/updates ternary-focused test cases covering chained/nested resolution and passthrough behavior.

Comment thread pkg/recipes/paramresolver/resolver.go
Comment thread pkg/recipes/paramresolver/resolver_test.go
@github-actions

Copy link
Copy Markdown

Unit Tests

    2 files  ±0    442 suites  ±0   7m 23s ⏱️ -10s
5 547 tests +5  5 545 ✅ +5  2 💤 ±0  0 ❌ ±0 
6 727 runs  +5  6 725 ✅ +5  2 💤 ±0  0 ❌ ±0 

Results for commit ce2fd90. ± Comparison against base commit 547cf38.

This pull request removes 1 and adds 6 tests. Note that renamed tests count towards both.
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/nested/chained_ternary_left_as-is_(out_of_scope)
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/brace-wrapped_nested_ternary_left_as-is_(outer_scanner_stops_at_first_brace)
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/chained_ternary_-_default_branch_matches
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/chained_ternary_-_first_branch_matches
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/chained_ternary_-_middle_branch_matches
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/chained_ternary_with_unresolvable_condition_on_chosen_branch_left_as-is
github.com/radius-project/radius/pkg/recipes/paramresolver ‑ Test_TernaryExpressions/nested_ternary_in_true_arm

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.83%. Comparing base (547cf38) to head (ce2fd90).

Files with missing lines Patch % Lines
pkg/recipes/paramresolver/resolver.go 90.00% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12239      +/-   ##
==========================================
+ Coverage   52.82%   52.83%   +0.01%     
==========================================
  Files         743      743              
  Lines       47788    47828      +40     
==========================================
+ Hits        25242    25269      +27     
- Misses      20195    20202       +7     
- Partials     2351     2357       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@willdavsmith willdavsmith added this pull request to the merge queue Jun 24, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 24, 2026
@willdavsmith willdavsmith added this pull request to the merge queue Jun 24, 2026
Merged via the queue into main with commit 42e7c4f Jun 25, 2026
76 of 77 checks passed
@willdavsmith willdavsmith deleted the willdavsmith-willdavsmith-recipe-ternary-resolver branch June 25, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants