Skip to content

Add YAML-validity tests for all generated workflows (regression guard for #407) - #430

Merged
sk593 merged 9 commits into
mainfrom
sk593-fix-deploy-workflow-yaml-quoting
Aug 20, 2026
Merged

sk593 merged 9 commits into
mainfrom
sk593-fix-deploy-workflow-yaml-quoting

Conversation

@sk593

@sk593 sk593 commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Why

Every Azure/AWS deploy dispatch was failing upstream with HTTP 422 because the generated deploy workflow files were invalid YAML (issue #407). This PR closes both gaps that let that ship: tests now parse rendered workflow YAML, and production generation now rejects invalid YAML before it can be committed.

Root cause — nested quotes. The deploy generator injects GitHub Actions expressions whose string defaults are single-quoted (GHA requires single quotes for string literals):

${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}
${{ vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' }}

When the upstream template wraps that placeholder in a single-quoted YAML scalar — TARGET_CLUSTER_ARCH_MODE: '{{TARGET_CLUSTER_ARCH_MODE}}' — the injected single quotes nest, YAML terminates the scalar early, and the rendered file is invalid. Double-quoting the scalar fixes it (source fix: radius-project/radius#12721).

Why current tests missed it. packages/core/src/workflows/deploy.test.ts rendered inline fixtures that were already double-quoted and asserted only with .toContain(...) substring checks. It never parsed the rendered output as YAML or exercised the real upstream templates, so it stayed green while the committed Radius templates produced invalid YAML after substitution.

What

  • Add yaml to parse rendered workflow output.
  • Generation-time validation: adapter-canvas parses final verify, deploy, and delete workflows after all core rendering and adapter transformations. Invalid upstream templates or quote-bearing ENV/APP_FILE values now fail locally with a file-specific error before any workflow is committed, instead of surfacing later as GitHub Actions HTTP 422. Validation stays in the adapter so @radius-project/core retains no runtime dependencies; the extension build aliases yaml to its pure-ESM browser parser entry so the generated artifact remains loadable.
  • Positive hermetic test: realistic fixtures (single-quoted APP_FILE/ENV, double-quoted arch scalars) render valid YAML for all deploy files, and injected GHA arch expressions survive substitution intact.
  • Negative regression guard: a single-quoted arch scalar renders invalid YAML, locking in why the scalar must be double-quoted and documenting [Bug]: Invalid YAML in deploy workflows — all dispatches fail with HTTP 422 #407.
  • Input regression coverage: quote-bearing environment names and application-file paths are rejected before commit across verify, deploy, and delete generation.
  • Live upstream coverage: workflow-yaml.live.test.ts fetches templates from radius-project/radius@main, renders deploy, delete, and verify workflows, and parses every result. The existing OIDC environment-contract live suite runs alongside it.
  • Visible, non-required CI: .github/workflows/live-tests.yml runs the live suites on every PR, pushes to main, nightly, and manually. It is intentionally separate from the hermetic Build pipeline and is not listed as a required status check in the active main ruleset, so upstream/network failures are visible without blocking unrelated merges.
  • Add a radius patch changeset for the new runtime validation behavior.

Testing

  • Focused adapter tests: 23 passed.
  • Live upstream suites: 6 passed against current Radius main.
  • Runtime integration: 11 passed.
  • Built-extension smoke: 6 passed after bundling the YAML parser as pure ESM.
  • Typecheck, lint, formatting, frozen-lockfile install, build, workflow-YAML parse, and diff checks pass.
  • The full local coverage run reached 4,066 passing tests but its existing Bicep subprocess tests cannot load the Node 25 dylib on this machine; CI uses the repository-required Node 24 runtime.

References

…ard for #407)

The deploy-workflow generator injects GitHub Actions expressions whose string
defaults are single-quoted (${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}).
When an upstream template wraps such a placeholder in a single-quoted YAML
scalar, the injected quotes nest and the rendered file becomes invalid YAML, so
every Azure/AWS deploy dispatch fails upstream with HTTP 422 (issue #407).

The existing deploy.test.ts used inline, already-double-quoted fixtures and
asserted only with .toContain(...) substring checks -- it never parsed the
rendered output as YAML nor exercised the real templates, so it stayed green
against invalid output.

Add consumer-side regression coverage in @radius-project/core:
- yaml devDependency (dev-only; core ships no runtime deps).
- Positive test: fixtures mirroring the real templates' quoting render valid
  YAML for all three files, and the injected GHA arch expressions survive
  substitution intact.
- Negative guard: a single-quoted arch scalar renders invalid YAML, locking in
  why the scalar must be double-quoted.
- Opt-in live test (RUN_LIVE_WORKFLOW_TESTS) that fetches the real upstream
  templates from radius-project/radius@main, renders them, and asserts valid
  YAML -- the only test that would have caught the radius#12640 regression.

Relates to #407; source fix
radius-project/radius#12721; regression origin radius-project/radius#12640.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
Copilot AI lite review requested due to automatic review settings August 19, 2026 18:43
@sk593
sk593 requested review from a team as code owners August 19, 2026 18:43
…flow-yaml-quoting

Signed-off-by: sk593 <shruthikumar@microsoft.com>

# Conflicts:
#	pnpm-lock.yaml
@github-actions

github-actions Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
See the Details below.

License Issues

packages/adapter-canvas/package.json

PackageVersionLicenseIssue Type
yamlcatalog:NullUnknown License

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/checkout 3d3c42e5aac5ba805825da76410c181273ba90b1 🟢 7
Details
CheckScoreReason
Maintained🟢 1024 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Code-Review🟢 10all changesets reviewed
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
Packaging⚠️ -1packaging workflow not detected
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Pinned-Dependencies⚠️ 3dependency not pinned by hash detected -- score normalized to 3
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection🟢 6branch protection is not maximal on development and all release branches
actions/actions/setup-node 820762786026740c76f36085b0efc47a31fe5020 🟢 6.4
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained🟢 1021 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 9binaries present in source code
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies🟢 5dependency not pinned by hash detected -- score normalized to 5
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 10SAST tool is run on all commits
actions/pnpm/action-setup 0977fd99725f1db4007ccb2928dbb4e90d06cc86 🟢 5.3
Details
CheckScoreReason
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review🟢 5Found 15/30 approved changesets -- score normalized to 5
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 86 commit(s) and 4 issue activity found in the last 90 days -- score normalized to 8
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies🟢 10all dependencies are pinned
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Fuzzing⚠️ 0project is not fuzzed
Security-Policy⚠️ 0security policy file not detected
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/yaml catalog: UnknownUnknown
npm/yaml catalog: UnknownUnknown

Scanned Files

  • .github/workflows/live-tests.yml
  • packages/adapter-canvas/package.json
  • packages/core/package.json

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

Adds regression coverage in @radius-project/core to ensure generated deploy workflow files are valid YAML, specifically guarding against the nested-quote failure mode that caused deploy dispatch HTTP 422 failures in #407 when GitHub Actions expressions with single-quoted defaults were injected into single-quoted YAML scalars.

Changes:

  • Add yaml as a devDependency (via workspace catalog) so tests can parse rendered workflows as YAML.
  • Extend deploy.test.ts with YAML-parse assertions (positive + explicit negative regression guard for the single-quoted arch scalar case).
  • Add an opt-in live test that fetches current upstream Radius templates at radius-project/radius@main, renders them, and asserts YAML validity when RUN_LIVE_WORKFLOW_TESTS is set.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pnpm-workspace.yaml Adds yaml to the workspace catalog for test-only YAML parsing.
pnpm-lock.yaml Records the catalog/importer entry for yaml in the lockfile.
packages/core/package.json Adds yaml as a devDependency for @radius-project/core.
packages/core/src/workflows/deploy.test.ts Adds hermetic YAML-validity tests (including a negative regression guard reproducing #407).
packages/core/src/workflows/deploy-yaml.live.test.ts Adds an opt-in live test that validates YAML parsing against upstream templates fetched from radius-project/radius.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pnpm-workspace.yaml Outdated
@github-actions

github-actions Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7c34dd1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
radius Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

The live regression test only rendered the three deploy workflows. Extend it to
also render the delete (delete-application/azure/aws.yml) and verify
(verify-azure/aws.yml) generators from their real upstream templates and assert
every generated file parses as valid YAML, so a malformed scalar, indentation,
or quoting change in ANY shipped workflow template is caught -- not just the
deploy arch-quote bug (#407).

Rename deploy-yaml.live.test.ts -> workflow-yaml.live.test.ts to reflect the
broader scope. Still opt-in via RUN_LIVE_WORKFLOW_TESTS.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
@sk593 sk593 changed the title Add YAML-validity tests for generated deploy workflows (regression guard for #407) Add YAML-validity tests for all generated workflows (regression guard for #407) Aug 19, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
@sk593

sk593 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

No changeset needed. This PR is test-only plus a dev-only yaml devDependency, and it touches only @radius-project/core, which is in the changesets ignore list in .changeset/config.json. Only the publishable radius plugin is versioned via changesets, and this PR doesn't change it — so there's no consumer-facing version bump to record.

sk593 and others added 3 commits August 19, 2026 13:02
Fixes the failing 'Build plugin dist > Check formatting' CI step; deploy.test.ts and workflow-yaml.live.test.ts did not match the repo Prettier config.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
The `*.live.test.ts` suites (workflow-yaml + oidc-environment-contract) are
opt-in via RUN_LIVE_WORKFLOW_TESTS and nothing ran them automatically, so the
upstream-contract canary they provide never fired. Add a scheduled workflow
(nightly + workflow_dispatch) that sets the flag and runs both live suites.

They stay out of the per-PR Build suite on purpose: they fetch templates from
radius-project/radius@main over the network, so a regression pushed upstream
would otherwise turn unrelated ai-extensions PRs red. On a schedule, a failure
instead flags an upstream template regression (like the invalid-YAML deploy bug
in #407) for triage without blocking contributors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
Make the upstream-contract canary easily flagged: the live suites now run as a
required check on pull_request and push to main (still nightly + on demand too),
so an invalid-YAML or shape regression in the radius-project/radius templates
surfaces as a failed check right away instead of only within 24h.

They remain a separate workflow from the hermetic Build suite, which stays
offline and deterministic.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>

@nicolejms nicolejms 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.

two design questions on where the YAML-validity guard belongs.

Comment thread packages/core/src/workflows/deploy.test.ts
Comment thread packages/core/src/workflows/deploy.test.ts
Parse final verify, deploy, and delete workflow output in the Canvas adapter after all rendering and transformations. Invalid upstream templates or quote-bearing ENV/APP_FILE inputs now fail locally with a file-specific error instead of being committed and rejected later by GitHub Actions with HTTP 422.

Keep core dependency-free by locating runtime validation in adapter-canvas. Bundle yaml's browser ESM entry so the generated extension remains loadable, and cover invalid environment/app-file scalars across all workflow generators.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
@sk593
sk593 merged commit e261225 into main Aug 20, 2026
9 checks passed
@sk593
sk593 deleted the sk593-fix-deploy-workflow-yaml-quoting branch August 20, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants