Architecture-aware container image builds in deploy workflows - #12640
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
This PR updates the shared run-rad-commands deploy workflows/actions to compute an effective container image build platform list based on the target cluster’s node architectures, so single-arch clusters avoid slow/flaky QEMU-emulated multi-arch builds while mixed/undetermined clusters keep the multi-arch fallback.
Changes:
- Add
compute-build-platforms.sh(and tests) to resolve effective build platforms from mode/fallback/detected arches. - Wire new
build-arch-mode/build-fallback-platformsinputs through the composite action and Azure/AWS workflow templates. - Inject
--parameters platforms=...only when the app declaresplatformsand it’s not already provided viaRADIUS_DEPLOY_PARAMS; add Make/test coverage.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| build/test.mk | Adds test-build-platforms and includes it in make test. |
| .github/extension/run-rad-commands-azure.yml | Adds template placeholders and passes arch inputs to the shared action. |
| .github/extension/run-rad-commands-aws.yml | Adds template placeholders and passes arch inputs to the shared action. |
| .github/extension/actions/run-rad-commands/action.yml | Declares new inputs and adds a step to detect/compute/export RADIUS_EFFECTIVE_BUILD_PLATFORMS. |
| .github/extension/actions/run-rad-commands/compute-build-platforms.sh | New platform resolver script (mode/fallback/detect logic). |
| .github/extension/actions/run-rad-commands/compute-build-platforms_test.sh | New unit + wiring tests for resolver/workflows/action. |
| .github/extension/actions/run-rad-commands/deploy-parameters.sh | Injects platforms parameter when applicable. |
| .github/extension/actions/run-rad-commands/deploy-parameters_test.sh | Adds coverage for platforms injection behavior. |
Suppressed comments (2)
.github/extension/actions/run-rad-commands/action.yml:113
- This currently suppresses kubectl errors and can hide useful diagnostics (RBAC, connectivity, etc.). Prefer letting stderr through while still degrading to empty ARCHES via
|| true. Also avoidecho "$ARCHES"in command substitution (it can mangle backslashes); use printf instead.
ARCHES=$(kubectl --kubeconfig "$TARGET_KUBECONFIG" get nodes \
-o jsonpath='{range .items[*]}{.status.nodeInfo.architecture}{"\n"}{end}' \
2>/dev/null || true)
echo "Detected target cluster node architectures: $(echo "$ARCHES" | tr '\n' ' ')"
.github/extension/actions/run-rad-commands/compute-build-platforms.sh:66
sortis locale-dependent; without pinning the locale, the "sorted (deterministic) order" guarantee can vary across runner images/locales. Set LC_ALL=C for stable collation.
sort -u |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12640 +/- ##
==========================================
+ Coverage 54.31% 54.32% +0.01%
==========================================
Files 770 770
Lines 51240 51240
==========================================
+ Hits 27829 27837 +8
+ Misses 20795 20791 -4
+ Partials 2616 2612 -4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thanks for the review. Addressed in bd0005c.
The |
bd0005c to
84c8ca4
Compare
Radius.Compute/containerImages builds run in an in-cluster BuildKit compiled for the runner's architecture (amd64 on standard GitHub-hosted runners). When an app leaves build.platforms unset the recipe defaults to multi-arch (linux/amd64,linux/arm64), so the arm64 half builds under QEMU emulation -- much slower and prone to emulation crashes. This lets the deploy workflows build only the platform(s) the target cluster actually runs. Contract (consumed by ai-extensions, radius-project/ai-extensions#300) via two template placeholders on the Azure and AWS run-rad-commands workflows: {{TARGET_CLUSTER_ARCH_MODE}} -> vars.RADIUS_BUILD_ARCH_MODE || 'detect' {{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}} -> vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' Behavior (compute-build-platforms.sh, invoked by the shared run-rad-commands action after the target kubeconfig is configured and before the deploy): - mode 'detect', single-arch cluster -> build that one platform (no emulation) - mode 'detect', mixed or undetermined -> fallback platform list - explicit platform list (contains '/') -> honored verbatim, no detection - empty / unsubstituted placeholder -> feature off, recipe default applies The computed list is exported as RADIUS_EFFECTIVE_BUILD_PLATFORMS and injected as `--parameters platforms=<list>` only when the app declares a `platforms` parameter and it was not already supplied via RADIUS_DEPLOY_PARAMS, flowing through the same conditional path as the existing app-image parameter. Apps that do not opt in are unaffected. Tests: - compute-build-platforms_test.sh: mode/detection/override/fallback matrix plus workflow and action wiring assertions (make test-build-platforms). - deploy-parameters_test.sh: platforms injection is gated on declaration, an empty computed list, and RADIUS_DEPLOY_PARAMS precedence. Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
…comment - action.yml: default-expand RADIUS_TARGET_KUBECONFIG so an unset value does not trip set -u before the empty case is handled (degrades to fallback). - compute-build-platforms.sh: rewrite the malformed MODE contract header comment (unfinished parenthesis / broken bullet) for readability. Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
77182df to
1780765
Compare
Radius functional test overviewClick here to see the test run details
Test Status⌛ Building Radius and pushing container images for functional tests... |
…ng) (radius-project#12721) ## Summary These deploy workflow templates are rendered by substituting `{{...}}` placeholders. For the architecture placeholders, the tooling injects a GitHub Actions expression whose string-literal default is **single-quoted** (GHA requires single quotes for string literals): ``` ${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }} ``` But these templates wrapped the placeholder scalar in **single** quotes: ```yaml TARGET_CLUSTER_ARCH_MODE: '{{TARGET_CLUSTER_ARCH_MODE}}' ``` After substitution the quotes nest and YAML terminates the scalar early, producing invalid YAML: ```yaml TARGET_CLUSTER_ARCH_MODE: '${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}' ``` Every Azure and AWS deploy dispatch then failed with `HTTP 422: failed to parse workflow ... error in your yaml syntax`. ## Fix In both `.github/extension/run-rad-commands-azure.yml` and `.github/extension/run-rad-commands-aws.yml`, the two architecture placeholder scalars are changed from single to **double** quotes: ```yaml TARGET_CLUSTER_ARCH_MODE: "{{TARGET_CLUSTER_ARCH_MODE}}" TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS: "{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}" ``` The rendered result then becomes valid YAML — the outer double quotes safely contain the inner single-quoted GHA string literal: ```yaml TARGET_CLUSTER_ARCH_MODE: "${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}" ``` The other single-quoted scalars (`APP_FILE: '{{APP_FILE}}'`, `default: '{{ENV}}'`, `environment: ${{ inputs.environment || '{{ENV}}' }}`) inject plain values that contain no single quotes, so they remain valid and are left unchanged. ## Verification - Both provider files parse as valid YAML. - Simulated the substitution (`{{TARGET_CLUSTER_ARCH_MODE}}` → `${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}` and `{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}` → `${{ vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' }}`) in temp copies of each file and confirmed they still parse as valid YAML, with the GHA expression preserved intact as the string value. ## Related - Regression introduced by the single-quoting in radius-project#12640 Signed-off-by: sk593 <shruthikumar@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
….1 (#12831) Patch release `v0.60.1` for channel `0.60`. ## Backported PRs Listed in cherry-pick (topological) order. **Gap filler** = not requested for the patch, but included because it touched the same region of a shared file between `release/0.60` and a requested commit. Without it the cherry-pick has no common merge base and conflicts. | # | Commit | PR | Role | | --- | --- | --- | --- | | 1 | `6882b5fdf` | #12640 | Requested | | 2 | `af0443809` | #12721 | Requested | | 3 | `7c6de043d` | #12733 | **Gap filler** — `.github/workflows/codeql.yml` | | 4 | `ca7b5a3e7` | #12728 | Requested | | 5 | `2eac7c1f1` | #12702 | Requested | | 6 | `b4ebe2f28` | #12751 | Requested | | 7 | `e30a2594d` | #12727 | Requested | | 8 | `3c7dfecd6` | #12769 | **Gap filler** — `.github/workflows/codeql.yml` | | 9 | `787b6ca1a` | #12765 | **Gap filler** — `build/test.mk` | | 10 | `f84184955` | #12775 | Requested | | 11 | `415d5b9c2` | #12779 | Requested | | 12 | `a1d976013` | #12782 | Requested | | 13 | `14a21bd0d` | #12786 | **Gap filler** — `build/test.mk` | | 14 | `a87146c77` | #12764 | **Gap filler** — `build/test.mk` | | 15 | `073ca3cfa` | #12758 | Requested | | 16 | `5e1f94c13` | #12785 | **Gap filler** — `pkg/cli/cmd/install/kubernetes/kubernetes.go` | | 17 | `1fd650af5` | #12742 | Requested | | 18 | `19376c3f3` | #12829 | Requested | Five of the six gap fillers are CI/test-only. #12785 is not: it adds a Helm `control-plane-readiness` Job, a `pre-upgrade wait-for-control-plane` command, UCP readiness probes, and a NetworkPolicy change. It is required for #12829 to apply. ## Validation Full chain dry-run cherry-picked onto `release/0.60` with zero conflicts. `go build ./...` clean; `pkg/cli/cmd/install/kubernetes`, `cmd/pre-upgrade/cmd`, `pkg/cli/...`, `pkg/graph/...`, and `test/validation` all pass. --------- Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
## Description `Radius.Compute/containerImages` builds run in an in-cluster BuildKit that is compiled for the runner's architecture (amd64 on standard GitHub-hosted runners). When an app leaves `build.platforms` unset, the recipe defaults to a multi-arch build (`linux/amd64,linux/arm64`), so the arm64 half is produced under QEMU emulation, which is roughly an order of magnitude slower and prone to emulation crashes (see the analysis on #12595). This PR lets the Azure and AWS deploy workflows build only the platform(s) the target cluster actually runs, while preserving multi-arch when it is genuinely needed. This is the upstream half of the contract in radius-project/ai-extensions#300. ## Contract Two template placeholders on the Azure and AWS `run-rad-commands` workflows, rendered by the extension: | Placeholder | Extension default | | --- | --- | | `{{TARGET_CLUSTER_ARCH_MODE}}` | `${{ vars.RADIUS_BUILD_ARCH_MODE \|\| 'detect' }}` | | `{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}` | `${{ vars.RADIUS_BUILD_PLATFORMS \|\| 'linux/amd64,linux/arm64' }}` | ## Behavior Resolved by `compute-build-platforms.sh`, invoked by the shared `run-rad-commands` action after the target kubeconfig is configured and before the app is built/deployed: | Mode | Cluster | Result | | --- | --- | --- | | `detect` | single-arch | build that one platform (no emulation) | | `detect` | mixed / undetermined | fallback platform list | | explicit list (contains `/`, e.g. `linux/amd64`) | n/a | honored verbatim, no detection | | empty / unsubstituted placeholder | n/a | feature off, recipe default applies (existing behavior) | The computed list is exported as `RADIUS_EFFECTIVE_BUILD_PLATFORMS` and injected as `--parameters platforms=<list>` only when the app declares a `platforms` parameter and it was not already supplied via `RADIUS_DEPLOY_PARAMS`, flowing through the same conditional path as the existing `app-image` parameter. Apps that do not declare `platforms`, and templates that do not render the placeholders, are unaffected. **Mixed-arch is a first-class outcome, not an error.** A cluster with both amd64 and arm64 nodes resolves to the fallback multi-arch list so images stay portable; only single-arch clusters drop to a single platform to skip emulation. ## Design notes - Kept the contract small and explicit. Recognized modes are `detect`, an explicit platform list, or empty/placeholder (off). An unrecognized keyword fails safe to the fallback list with a warning, documented in the workflow and script comments. - Detection uses `kubectl get nodes -o jsonpath=...nodeInfo.architecture`; a failed probe degrades to empty, which the resolver treats as "undetermined" and maps to the fallback. - No Radius runtime/CLI changes; this is entirely in the deploy workflow templates, the shared composite action, and a helper script. ## Testing - `make test-build-platforms` — new `compute-build-platforms_test.sh`: mode/detection/override/fallback matrix (single-arch, mixed-arch, unknown-arch, x86_64/aarch64 aliases, normalization, unrecognized mode), plus assertions that both workflows carry the placeholders and wire the inputs, and that the action declares the inputs and runs the helper. - `make test-run-rad-commands-action` — extended `deploy-parameters_test.sh`: `platforms` is injected only when declared, skipped when no effective list was computed, and never overrides a `RADIUS_DEPLOY_PARAMS`-supplied value. - `shellcheck` clean on all shell; all three modified YAML files parse. ## Files | File | Change | | --- | --- | | `.github/extension/actions/run-rad-commands/compute-build-platforms.sh` | New: resolves effective build platforms from mode/fallback/detected arches | | `.github/extension/actions/run-rad-commands/compute-build-platforms_test.sh` | New: unit + wiring tests | | `.github/extension/actions/run-rad-commands/action.yml` | New `build-arch-mode` / `build-fallback-platforms` inputs; detection step exporting `RADIUS_EFFECTIVE_BUILD_PLATFORMS` | | `.github/extension/actions/run-rad-commands/deploy-parameters.sh` | Inject `platforms` when the app declares it and a list was computed | | `.github/extension/actions/run-rad-commands/deploy-parameters_test.sh` | Coverage for the injection | | `.github/extension/run-rad-commands-azure.yml` / `-aws.yml` | Placeholders + pass the two inputs to `run-rad-commands` | | `build/test.mk` | `test-build-platforms` target, added to `test` | ## Related - radius-project/ai-extensions#300 - #12595 --------- Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com> (cherry picked from commit 6882b5f) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
…ng) (#12721) ## Summary These deploy workflow templates are rendered by substituting `{{...}}` placeholders. For the architecture placeholders, the tooling injects a GitHub Actions expression whose string-literal default is **single-quoted** (GHA requires single quotes for string literals): ``` ${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }} ``` But these templates wrapped the placeholder scalar in **single** quotes: ```yaml TARGET_CLUSTER_ARCH_MODE: '{{TARGET_CLUSTER_ARCH_MODE}}' ``` After substitution the quotes nest and YAML terminates the scalar early, producing invalid YAML: ```yaml TARGET_CLUSTER_ARCH_MODE: '${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}' ``` Every Azure and AWS deploy dispatch then failed with `HTTP 422: failed to parse workflow ... error in your yaml syntax`. ## Fix In both `.github/extension/run-rad-commands-azure.yml` and `.github/extension/run-rad-commands-aws.yml`, the two architecture placeholder scalars are changed from single to **double** quotes: ```yaml TARGET_CLUSTER_ARCH_MODE: "{{TARGET_CLUSTER_ARCH_MODE}}" TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS: "{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}" ``` The rendered result then becomes valid YAML — the outer double quotes safely contain the inner single-quoted GHA string literal: ```yaml TARGET_CLUSTER_ARCH_MODE: "${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}" ``` The other single-quoted scalars (`APP_FILE: '{{APP_FILE}}'`, `default: '{{ENV}}'`, `environment: ${{ inputs.environment || '{{ENV}}' }}`) inject plain values that contain no single quotes, so they remain valid and are left unchanged. ## Verification - Both provider files parse as valid YAML. - Simulated the substitution (`{{TARGET_CLUSTER_ARCH_MODE}}` → `${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}` and `{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}` → `${{ vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' }}`) in temp copies of each file and confirmed they still parse as valid YAML, with the GHA expression preserved intact as the string value. ## Related - Regression introduced by the single-quoting in #12640 Signed-off-by: sk593 <shruthikumar@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> (cherry picked from commit af04438) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
….1 (#12831) Patch release `v0.60.1` for channel `0.60`. ## Backported PRs Listed in cherry-pick (topological) order. **Gap filler** = not requested for the patch, but included because it touched the same region of a shared file between `release/0.60` and a requested commit. Without it the cherry-pick has no common merge base and conflicts. | # | Commit | PR | Role | | --- | --- | --- | --- | | 1 | `6882b5fdf` | #12640 | Requested | | 2 | `af0443809` | #12721 | Requested | | 3 | `7c6de043d` | #12733 | **Gap filler** — `.github/workflows/codeql.yml` | | 4 | `ca7b5a3e7` | #12728 | Requested | | 5 | `2eac7c1f1` | #12702 | Requested | | 6 | `b4ebe2f28` | #12751 | Requested | | 7 | `e30a2594d` | #12727 | Requested | | 8 | `3c7dfecd6` | #12769 | **Gap filler** — `.github/workflows/codeql.yml` | | 9 | `787b6ca1a` | #12765 | **Gap filler** — `build/test.mk` | | 10 | `f84184955` | #12775 | Requested | | 11 | `415d5b9c2` | #12779 | Requested | | 12 | `a1d976013` | #12782 | Requested | | 13 | `14a21bd0d` | #12786 | **Gap filler** — `build/test.mk` | | 14 | `a87146c77` | #12764 | **Gap filler** — `build/test.mk` | | 15 | `073ca3cfa` | #12758 | Requested | | 16 | `5e1f94c13` | #12785 | **Gap filler** — `pkg/cli/cmd/install/kubernetes/kubernetes.go` | | 17 | `1fd650af5` | #12742 | Requested | | 18 | `19376c3f3` | #12829 | Requested | Five of the six gap fillers are CI/test-only. #12785 is not: it adds a Helm `control-plane-readiness` Job, a `pre-upgrade wait-for-control-plane` command, UCP readiness probes, and a NetworkPolicy change. It is required for #12829 to apply. ## Validation Full chain dry-run cherry-picked onto `release/0.60` with zero conflicts. `go build ./...` clean; `pkg/cli/cmd/install/kubernetes`, `cmd/pre-upgrade/cmd`, `pkg/cli/...`, `pkg/graph/...`, and `test/validation` all pass. --------- Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> (cherry picked from commit e5938bf) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
## Description `Radius.Compute/containerImages` builds run in an in-cluster BuildKit that is compiled for the runner's architecture (amd64 on standard GitHub-hosted runners). When an app leaves `build.platforms` unset, the recipe defaults to a multi-arch build (`linux/amd64,linux/arm64`), so the arm64 half is produced under QEMU emulation, which is roughly an order of magnitude slower and prone to emulation crashes (see the analysis on #12595). This PR lets the Azure and AWS deploy workflows build only the platform(s) the target cluster actually runs, while preserving multi-arch when it is genuinely needed. This is the upstream half of the contract in radius-project/ai-extensions#300. ## Contract Two template placeholders on the Azure and AWS `run-rad-commands` workflows, rendered by the extension: | Placeholder | Extension default | | --- | --- | | `{{TARGET_CLUSTER_ARCH_MODE}}` | `${{ vars.RADIUS_BUILD_ARCH_MODE \|\| 'detect' }}` | | `{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}` | `${{ vars.RADIUS_BUILD_PLATFORMS \|\| 'linux/amd64,linux/arm64' }}` | ## Behavior Resolved by `compute-build-platforms.sh`, invoked by the shared `run-rad-commands` action after the target kubeconfig is configured and before the app is built/deployed: | Mode | Cluster | Result | | --- | --- | --- | | `detect` | single-arch | build that one platform (no emulation) | | `detect` | mixed / undetermined | fallback platform list | | explicit list (contains `/`, e.g. `linux/amd64`) | n/a | honored verbatim, no detection | | empty / unsubstituted placeholder | n/a | feature off, recipe default applies (existing behavior) | The computed list is exported as `RADIUS_EFFECTIVE_BUILD_PLATFORMS` and injected as `--parameters platforms=<list>` only when the app declares a `platforms` parameter and it was not already supplied via `RADIUS_DEPLOY_PARAMS`, flowing through the same conditional path as the existing `app-image` parameter. Apps that do not declare `platforms`, and templates that do not render the placeholders, are unaffected. **Mixed-arch is a first-class outcome, not an error.** A cluster with both amd64 and arm64 nodes resolves to the fallback multi-arch list so images stay portable; only single-arch clusters drop to a single platform to skip emulation. ## Design notes - Kept the contract small and explicit. Recognized modes are `detect`, an explicit platform list, or empty/placeholder (off). An unrecognized keyword fails safe to the fallback list with a warning, documented in the workflow and script comments. - Detection uses `kubectl get nodes -o jsonpath=...nodeInfo.architecture`; a failed probe degrades to empty, which the resolver treats as "undetermined" and maps to the fallback. - No Radius runtime/CLI changes; this is entirely in the deploy workflow templates, the shared composite action, and a helper script. ## Testing - `make test-build-platforms` — new `compute-build-platforms_test.sh`: mode/detection/override/fallback matrix (single-arch, mixed-arch, unknown-arch, x86_64/aarch64 aliases, normalization, unrecognized mode), plus assertions that both workflows carry the placeholders and wire the inputs, and that the action declares the inputs and runs the helper. - `make test-run-rad-commands-action` — extended `deploy-parameters_test.sh`: `platforms` is injected only when declared, skipped when no effective list was computed, and never overrides a `RADIUS_DEPLOY_PARAMS`-supplied value. - `shellcheck` clean on all shell; all three modified YAML files parse. ## Files | File | Change | | --- | --- | | `.github/extension/actions/run-rad-commands/compute-build-platforms.sh` | New: resolves effective build platforms from mode/fallback/detected arches | | `.github/extension/actions/run-rad-commands/compute-build-platforms_test.sh` | New: unit + wiring tests | | `.github/extension/actions/run-rad-commands/action.yml` | New `build-arch-mode` / `build-fallback-platforms` inputs; detection step exporting `RADIUS_EFFECTIVE_BUILD_PLATFORMS` | | `.github/extension/actions/run-rad-commands/deploy-parameters.sh` | Inject `platforms` when the app declares it and a list was computed | | `.github/extension/actions/run-rad-commands/deploy-parameters_test.sh` | Coverage for the injection | | `.github/extension/run-rad-commands-azure.yml` / `-aws.yml` | Placeholders + pass the two inputs to `run-rad-commands` | | `build/test.mk` | `test-build-platforms` target, added to `test` | ## Related - radius-project/ai-extensions#300 - #12595 --------- Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com> (cherry picked from commit 6882b5f) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
…ng) (#12721) ## Summary These deploy workflow templates are rendered by substituting `{{...}}` placeholders. For the architecture placeholders, the tooling injects a GitHub Actions expression whose string-literal default is **single-quoted** (GHA requires single quotes for string literals): ``` ${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }} ``` But these templates wrapped the placeholder scalar in **single** quotes: ```yaml TARGET_CLUSTER_ARCH_MODE: '{{TARGET_CLUSTER_ARCH_MODE}}' ``` After substitution the quotes nest and YAML terminates the scalar early, producing invalid YAML: ```yaml TARGET_CLUSTER_ARCH_MODE: '${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}' ``` Every Azure and AWS deploy dispatch then failed with `HTTP 422: failed to parse workflow ... error in your yaml syntax`. ## Fix In both `.github/extension/run-rad-commands-azure.yml` and `.github/extension/run-rad-commands-aws.yml`, the two architecture placeholder scalars are changed from single to **double** quotes: ```yaml TARGET_CLUSTER_ARCH_MODE: "{{TARGET_CLUSTER_ARCH_MODE}}" TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS: "{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}" ``` The rendered result then becomes valid YAML — the outer double quotes safely contain the inner single-quoted GHA string literal: ```yaml TARGET_CLUSTER_ARCH_MODE: "${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}" ``` The other single-quoted scalars (`APP_FILE: '{{APP_FILE}}'`, `default: '{{ENV}}'`, `environment: ${{ inputs.environment || '{{ENV}}' }}`) inject plain values that contain no single quotes, so they remain valid and are left unchanged. ## Verification - Both provider files parse as valid YAML. - Simulated the substitution (`{{TARGET_CLUSTER_ARCH_MODE}}` → `${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}` and `{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}` → `${{ vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' }}`) in temp copies of each file and confirmed they still parse as valid YAML, with the GHA expression preserved intact as the string value. ## Related - Regression introduced by the single-quoting in #12640 Signed-off-by: sk593 <shruthikumar@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> (cherry picked from commit af04438) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
….1 (#12831) Patch release `v0.60.1` for channel `0.60`. ## Backported PRs Listed in cherry-pick (topological) order. **Gap filler** = not requested for the patch, but included because it touched the same region of a shared file between `release/0.60` and a requested commit. Without it the cherry-pick has no common merge base and conflicts. | # | Commit | PR | Role | | --- | --- | --- | --- | | 1 | `6882b5fdf` | #12640 | Requested | | 2 | `af0443809` | #12721 | Requested | | 3 | `7c6de043d` | #12733 | **Gap filler** — `.github/workflows/codeql.yml` | | 4 | `ca7b5a3e7` | #12728 | Requested | | 5 | `2eac7c1f1` | #12702 | Requested | | 6 | `b4ebe2f28` | #12751 | Requested | | 7 | `e30a2594d` | #12727 | Requested | | 8 | `3c7dfecd6` | #12769 | **Gap filler** — `.github/workflows/codeql.yml` | | 9 | `787b6ca1a` | #12765 | **Gap filler** — `build/test.mk` | | 10 | `f84184955` | #12775 | Requested | | 11 | `415d5b9c2` | #12779 | Requested | | 12 | `a1d976013` | #12782 | Requested | | 13 | `14a21bd0d` | #12786 | **Gap filler** — `build/test.mk` | | 14 | `a87146c77` | #12764 | **Gap filler** — `build/test.mk` | | 15 | `073ca3cfa` | #12758 | Requested | | 16 | `5e1f94c13` | #12785 | **Gap filler** — `pkg/cli/cmd/install/kubernetes/kubernetes.go` | | 17 | `1fd650af5` | #12742 | Requested | | 18 | `19376c3f3` | #12829 | Requested | Five of the six gap fillers are CI/test-only. #12785 is not: it adds a Helm `control-plane-readiness` Job, a `pre-upgrade wait-for-control-plane` command, UCP readiness probes, and a NetworkPolicy change. It is required for #12829 to apply. ## Validation Full chain dry-run cherry-picked onto `release/0.60` with zero conflicts. `go build ./...` clean; `pkg/cli/cmd/install/kubernetes`, `cmd/pre-upgrade/cmd`, `pkg/cli/...`, `pkg/graph/...`, and `test/validation` all pass. --------- Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> (cherry picked from commit e5938bf) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
Description
Radius.Compute/containerImagesbuilds run in an in-cluster BuildKit that is compiled for the runner's architecture (amd64 on standard GitHub-hosted runners). When an app leavesbuild.platformsunset, the recipe defaults to a multi-arch build (linux/amd64,linux/arm64), so the arm64 half is produced under QEMU emulation, which is roughly an order of magnitude slower and prone to emulation crashes (see the analysis on #12595). This PR lets the Azure and AWS deploy workflows build only the platform(s) the target cluster actually runs, while preserving multi-arch when it is genuinely needed.This is the upstream half of the contract in radius-project/ai-extensions#300.
Contract
Two template placeholders on the Azure and AWS
run-rad-commandsworkflows, rendered by the extension:{{TARGET_CLUSTER_ARCH_MODE}}${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}${{ vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' }}Behavior
Resolved by
compute-build-platforms.sh, invoked by the sharedrun-rad-commandsaction after the target kubeconfig is configured and before the app is built/deployed:detectdetect/, e.g.linux/amd64)The computed list is exported as
RADIUS_EFFECTIVE_BUILD_PLATFORMSand injected as--parameters platforms=<list>only when the app declares aplatformsparameter and it was not already supplied viaRADIUS_DEPLOY_PARAMS, flowing through the same conditional path as the existingapp-imageparameter. Apps that do not declareplatforms, and templates that do not render the placeholders, are unaffected.Mixed-arch is a first-class outcome, not an error. A cluster with both amd64 and arm64 nodes resolves to the fallback multi-arch list so images stay portable; only single-arch clusters drop to a single platform to skip emulation.
Design notes
detect, an explicit platform list, or empty/placeholder (off). An unrecognized keyword fails safe to the fallback list with a warning, documented in the workflow and script comments.kubectl get nodes -o jsonpath=...nodeInfo.architecture; a failed probe degrades to empty, which the resolver treats as "undetermined" and maps to the fallback.Testing
make test-build-platforms— newcompute-build-platforms_test.sh: mode/detection/override/fallback matrix (single-arch, mixed-arch, unknown-arch, x86_64/aarch64 aliases, normalization, unrecognized mode), plus assertions that both workflows carry the placeholders and wire the inputs, and that the action declares the inputs and runs the helper.make test-run-rad-commands-action— extendeddeploy-parameters_test.sh:platformsis injected only when declared, skipped when no effective list was computed, and never overrides aRADIUS_DEPLOY_PARAMS-supplied value.shellcheckclean on all shell; all three modified YAML files parse.Files
.github/extension/actions/run-rad-commands/compute-build-platforms.sh.github/extension/actions/run-rad-commands/compute-build-platforms_test.sh.github/extension/actions/run-rad-commands/action.ymlbuild-arch-mode/build-fallback-platformsinputs; detection step exportingRADIUS_EFFECTIVE_BUILD_PLATFORMS.github/extension/actions/run-rad-commands/deploy-parameters.shplatformswhen the app declares it and a list was computed.github/extension/actions/run-rad-commands/deploy-parameters_test.sh.github/extension/run-rad-commands-azure.yml/-aws.ymlrun-rad-commandsbuild/test.mktest-build-platformstarget, added totestRelated