diff --git a/.github/scripts/build-summary.sh b/.github/scripts/build-summary.sh new file mode 100644 index 0000000000..45e27f3585 --- /dev/null +++ b/.github/scripts/build-summary.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# ============================================================================ +# Render the build job summary table shared by the trigger-scoped build +# workflows, and fail when any job did not succeed. +# +# Usage: +# build-summary.sh ":" [":" ...] +# +# A result of "success" or "skipped" passes; anything else fails the step. +# The table is appended to $GITHUB_STEP_SUMMARY when set, otherwise stdout. +# ============================================================================ + +set -euo pipefail + +usage() { + echo "Usage: $0 \":\" [\":\" ...]" >&2 +} + +main() { + if [[ $# -eq 0 ]]; then + echo "Error: at least one \":\" argument is required" >&2 + usage + exit 1 + fi + + local summary_file="${GITHUB_STEP_SUMMARY:-/dev/stdout}" + local failed=0 + local entry job result icon + + for entry in "$@"; do + job="${entry%%:*}" + result="${entry##*:}" + if [[ -z "${job}" || -z "${result}" || "${job}" == "${entry}" ]]; then + echo "Error: malformed argument '${entry}', expected \":\"" >&2 + exit 1 + fi + done + + { + echo "## Build Results Summary" + echo + echo "| Job | Status |" + echo "|-----|--------|" + + for entry in "$@"; do + job="${entry%%:*}" + result="${entry##*:}" + + if [[ "${result}" == "success" || "${result}" == "skipped" ]]; then + icon="✅" + else + icon="❌" + failed=1 + fi + + echo "| ${job} | ${icon} ${result} |" + done + + echo + if ((failed == 1)); then + echo "**Result: ❌ BUILD FAILED**" + else + echo "**Result: ✅ ALL BUILDS PASSED**" + fi + } >>"${summary_file}" + + if ((failed == 1)); then + echo "::error::One or more build jobs failed or were cancelled" >&2 + exit 1 + fi + + echo "All build jobs completed successfully" +} + +main "$@" diff --git a/.github/scripts/build-summary_test.sh b/.github/scripts/build-summary_test.sh new file mode 100644 index 0000000000..b2b89a3606 --- /dev/null +++ b/.github/scripts/build-summary_test.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# ============================================================================ +# Tests for .github/scripts/build-summary.sh +# ============================================================================ + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR +readonly SCRIPT="${SCRIPT_DIR}/build-summary.sh" + +TEST_ROOT="" +PASS=0 +FAIL=0 + +cleanup() { + if [[ -n "${TEST_ROOT}" && -d "${TEST_ROOT}" ]]; then + rm -rf "${TEST_ROOT}" + fi +} +trap cleanup EXIT + +fail_test() { + echo " ASSERT FAILED: $1" + ((++FAIL)) +} + +pass_test() { + ((++PASS)) +} + +# Run the script with a dedicated summary file and capture its exit code. +# Sets SUMMARY_FILE, STATUS, and OUTPUT for the caller to assert on. +run_summary() { + local name="$1" + shift + + SUMMARY_FILE="${TEST_ROOT}/${name}.md" + : >"${SUMMARY_FILE}" + + STATUS=0 + OUTPUT="$(GITHUB_STEP_SUMMARY="${SUMMARY_FILE}" bash "${SCRIPT}" "$@" 2>&1)" || STATUS=$? +} + +test_all_success_passes() { + run_summary "all-success" "build-check:success" "build-and-push-images:success" + + if [[ "${STATUS}" -ne 0 ]]; then + fail_test "expected exit 0 for all-success, got ${STATUS}" + return + fi + if ! grep -q "ALL BUILDS PASSED" "${SUMMARY_FILE}"; then + fail_test "summary is missing the success verdict" + return + fi + if ! grep -q "| build-check | ✅ success |" "${SUMMARY_FILE}"; then + fail_test "summary is missing the build-check row" + return + fi + pass_test +} + +test_skipped_counts_as_success() { + run_summary "skipped" "build-check:success" "build-and-push-bicep-types:skipped" + + if [[ "${STATUS}" -ne 0 ]]; then + fail_test "expected skipped to pass, got exit ${STATUS}" + return + fi + if ! grep -q "| build-and-push-bicep-types | ✅ skipped |" "${SUMMARY_FILE}"; then + fail_test "skipped job should render as passing" + return + fi + pass_test +} + +test_failure_fails() { + run_summary "failure" "build-check:success" "build-and-push-images:failure" + + if [[ "${STATUS}" -eq 0 ]]; then + fail_test "expected non-zero exit when a job failed" + return + fi + if ! grep -q "BUILD FAILED" "${SUMMARY_FILE}"; then + fail_test "summary is missing the failure verdict" + return + fi + if [[ "${OUTPUT}" != *"::error::"* ]]; then + fail_test "expected a workflow error annotation" + return + fi + pass_test +} + +test_cancelled_fails() { + run_summary "cancelled" "build-check:cancelled" + + if [[ "${STATUS}" -eq 0 ]]; then + fail_test "expected non-zero exit when a job was cancelled" + return + fi + pass_test +} + +test_malformed_argument_fails() { + run_summary "malformed" "build-check" + + if [[ "${STATUS}" -eq 0 ]]; then + fail_test "expected non-zero exit for a malformed argument" + return + fi + if [[ "${OUTPUT}" != *"malformed argument"* ]]; then + fail_test "expected a malformed-argument message, got: ${OUTPUT}" + return + fi + # A rejected argument must not leave a partial table behind. + if [[ -s "${SUMMARY_FILE}" ]]; then + fail_test "malformed input should not write a summary" + return + fi + pass_test +} + +test_no_arguments_fails() { + run_summary "no-args" + + if [[ "${STATUS}" -eq 0 ]]; then + fail_test "expected non-zero exit when no jobs were supplied" + return + fi + pass_test +} + +main() { + TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/build-summary-test-XXXXXX")" + + test_all_success_passes + test_skipped_counts_as_success + test_failure_fails + test_cancelled_fails + test_malformed_argument_fails + test_no_arguments_fails + + if ((FAIL > 0)); then + echo "build summary tests failed: ${PASS} passed, ${FAIL} failed" + exit 1 + fi + + echo "build summary tests passed (${PASS} tests)" +} + +main "$@" diff --git a/.github/workflows/__build-bicep-types.yaml b/.github/workflows/__build-bicep-types.yaml new file mode 100644 index 0000000000..cacd224017 --- /dev/null +++ b/.github/workflows/__build-bicep-types.yaml @@ -0,0 +1,106 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: __build-bicep-types + +on: + workflow_call: + +permissions: {} + +jobs: + build-and-push-bicep-types: + name: Dispatch Bicep Types publish + runs-on: ubuntu-24.04 + timeout-minutes: 15 + environment: publish-bicep + permissions: + contents: read # Required for actions/checkout + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version-file: .python-version + + - name: Parse release version and set environment variables + run: python ./.github/scripts/get_release_version.py + + - name: Get App Token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + id: get-token + with: + client-id: ${{ secrets.RADIUS_PUBLISHER_BOT_CLIENT_ID }} + private-key: ${{ secrets.RADIUS_PUBLISHER_BOT_PRIVATE_KEY }} + permission-metadata: read + permission-actions: read + permission-contents: write + owner: azure-octo + repositories: | + radius-publisher + + - name: Capture dispatch start time + id: dispatch-start + shell: bash + run: | + echo "started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" + + - name: Repository Dispatch + id: repository-dispatch + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 + with: + token: ${{ steps.get-token.outputs.token }} + repository: azure-octo/radius-publisher + event-type: bicep-types + client-payload: |- + { + "source_repository": "${{ github.repository }}", + "source_ref": "${{ github.ref }}", + "source_sha": "${{ github.sha }}", + "rel_channel": "${{ env.REL_CHANNEL }}", + "registry_target": "radius" + } + + - name: Monitor remote workflow + id: monitor-remote-workflow + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ steps.get-token.outputs.token }} + script: | + const { default: script } = await import(`${process.env.GITHUB_WORKSPACE}/.github/scripts/monitor-remote-workflow.mjs`) + await script({context, github, core}) + env: + INPUT_OWNER: azure-octo + INPUT_REPO: radius-publisher + INPUT_WORKFLOW_FILE: publish-bicep-types.yml + INPUT_DISPATCH_STARTED_AT: ${{ steps.dispatch-start.outputs.started_at }} + INPUT_MAX_WAIT_SECONDS: "600" + INPUT_POLL_INTERVAL_SECONDS: "15" + + - name: Show failed logs + if: failure() && steps.monitor-remote-workflow.outputs.run_id != '' + shell: bash + env: + GH_TOKEN: ${{ steps.get-token.outputs.token }} + RUN_ID: ${{ steps.monitor-remote-workflow.outputs.run_id }} + run: | + gh run view "${RUN_ID}" --repo azure-octo/radius-publisher --log-failed || true diff --git a/.github/workflows/__build-cli.yaml b/.github/workflows/__build-cli.yaml new file mode 100644 index 0000000000..d4077ee1bc --- /dev/null +++ b/.github/workflows/__build-cli.yaml @@ -0,0 +1,143 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: __build-cli + +on: + workflow_call: + inputs: + only_changed: + description: Whether only non-build files changed + required: true + type: string + +permissions: {} + +env: + CONTAINER_REGISTRY: ghcr.io/radius-project + RELEASE_PATH: ./release + IMAGE_SRC: https://github.com/radius-project/radius + +jobs: + build-and-push-cli: + name: Build ${{ matrix.target_os }}_${{ matrix.target_arch }} binaries + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + issues: read # Required for publishing test results + checks: write # Required for publishing test results + pull-requests: write # Required for publishing test results + env: + GOOS: ${{ matrix.target_os }} + GOARCH: ${{ matrix.target_arch }} + GOPROXY: https://proxy.golang.org + strategy: + fail-fast: false + matrix: + include: + - target_os: linux + target_arch: amd64 + - target_os: linux + target_arch: arm64 + - target_os: linux + target_arch: arm + - target_os: windows + target_arch: amd64 + - target_os: windows + target_arch: arm64 + - target_os: darwin + target_arch: amd64 + - target_os: darwin + target_arch: arm64 + steps: + - name: Checkout + if: inputs.only_changed != 'true' + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup Go + if: inputs.only_changed != 'true' + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: go.mod + cache-dependency-path: go.sum + cache: false + + - name: Setup Python + if: inputs.only_changed != 'true' + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version-file: .python-version + + - name: Parse release version and set environment variables + if: inputs.only_changed != 'true' + run: python ./.github/scripts/get_release_version.py + + - name: Make build + if: inputs.only_changed != 'true' + run: make build + + - name: Copy cli binaries to release (unix-like) + if: matrix.target_os != 'windows' && inputs.only_changed != 'true' + run: | + mkdir -p "${RELEASE_PATH}" + cp "./dist/${GOOS}_${GOARCH}/release/rad" "${RELEASE_PATH}/rad_${GOOS}_${GOARCH}" + + - name: Copy cli binaries to release (windows) + if: matrix.target_os == 'windows' && inputs.only_changed != 'true' + run: | + mkdir -p "${RELEASE_PATH}" + cp "./dist/${GOOS}_${GOARCH}/release/rad.exe" "${RELEASE_PATH}/rad_${GOOS}_${GOARCH}.exe" + + - name: Upload CLI binary + if: inputs.only_changed != 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: rad_cli_${{ matrix.target_os}}_${{ matrix.target_arch}} + path: ${{ env.RELEASE_PATH }} + + - name: Login to GitHub Container Registry + if: inputs.only_changed != 'true' + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Install oras + if: inputs.only_changed != 'true' + run: make install-oras + + - name: Push latest rad cli binary to GHCR (unix-like) + if: github.ref == 'refs/heads/main' && matrix.target_os != 'windows' && inputs.only_changed != 'true' + run: | + cp "./dist/${GOOS}_${GOARCH}/release/rad" ./rad + oras push "${CONTAINER_REGISTRY}/rad/${GOOS}-${GOARCH}:latest" ./rad --annotation "org.opencontainers.image.source=${IMAGE_SRC}" + + - name: Copy cli binaries to release (windows) + if: github.ref == 'refs/heads/main' && matrix.target_os == 'windows' && inputs.only_changed != 'true' + run: | + cp "./dist/${GOOS}_${GOARCH}/release/rad.exe" ./rad.exe + oras push "${CONTAINER_REGISTRY}/rad/${GOOS}-${GOARCH}:latest" ./rad.exe --annotation "org.opencontainers.image.source=${IMAGE_SRC}" + + - name: Skip + if: inputs.only_changed == 'true' + run: exit 0 diff --git a/.github/workflows/__build-helm-chart.yaml b/.github/workflows/__build-helm-chart.yaml new file mode 100644 index 0000000000..53ffbce4e0 --- /dev/null +++ b/.github/workflows/__build-helm-chart.yaml @@ -0,0 +1,71 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: __build-helm-chart + +on: + workflow_call: + +permissions: {} + +jobs: + build-and-push-helm-chart: + name: Helm chart build + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + env: + ARTIFACT_DIR: ./dist/Charts + HELM_PACKAGE_DIR: helm + HELM_CHARTS_DIR: deploy/Chart + OCI_REGISTRY: ghcr.io + OCI_REPOSITORY: radius-project/helm-chart + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install helm + run: make install-helm + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version-file: .python-version + + - name: Parse release version and set environment variables + run: python ./.github/scripts/get_release_version.py + + - name: Run Helm linter + run: helm lint "${HELM_CHARTS_DIR}" + + - name: Package Helm chart + run: | + mkdir -p "${ARTIFACT_DIR}/${HELM_PACKAGE_DIR}" + helm package "${HELM_CHARTS_DIR}" --version "${CHART_VERSION}" --app-version "${REL_VERSION}" --destination "${ARTIFACT_DIR}/${HELM_PACKAGE_DIR}" + + - name: Push helm chart to GHCR + env: + GH_TOKEN: ${{ github.token }} + GH_ACTOR: ${{ github.actor }} + run: | + echo "${GH_TOKEN}" | helm registry login -u "${GH_ACTOR}" --password-stdin "${OCI_REGISTRY}" + helm push "${ARTIFACT_DIR}/${HELM_PACKAGE_DIR}/radius-${CHART_VERSION}.tgz" "oci://${OCI_REGISTRY}/${OCI_REPOSITORY}" diff --git a/.github/workflows/__build-images.yaml b/.github/workflows/__build-images.yaml new file mode 100644 index 0000000000..a8419a5cfd --- /dev/null +++ b/.github/workflows/__build-images.yaml @@ -0,0 +1,126 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: __build-images + +on: + workflow_call: + +permissions: {} + +env: + CONTAINER_REGISTRY: ghcr.io/radius-project + +jobs: + build-and-push-images: + name: Build and publish container images + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version-file: .python-version + + - name: Parse release version and set environment variables + run: python ./.github/scripts/get_release_version.py + + - name: Setup Go + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: go.mod + cache-dependency-path: go.sum + cache: false + + - name: Login to GitHub Container Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Setup QEMU + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + - name: Build container images (PR) + if: github.event_name == 'pull_request' + run: make docker-build + env: + DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }}/dev + DOCKER_TAG_VERSION: ${{ env.REL_VERSION }} + DOCKER_CACHE_GHA: 1 + + - name: Save container images to artifacts (PR) + if: github.event_name == 'pull_request' + run: make docker-save-images + env: + DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }}/dev + DOCKER_TAG_VERSION: ${{ env.REL_VERSION }} + + - name: Upload container image artifacts (PR) + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: container-images-${{ env.REL_VERSION }} + path: ./dist/images/ + retention-days: 1 + + - name: Push container images (latest) + if: (github.ref == 'refs/heads/main') + run: make docker-multi-arch-push + env: + DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }} + DOCKER_TAG_VERSION: latest + DOCKER_CACHE_GHA: 1 + + - name: Push container images (release) + if: startsWith(github.ref, 'refs/tags/v') + run: make docker-multi-arch-push + env: + DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }} + DOCKER_TAG_VERSION: ${{ env.REL_CHANNEL }} + DOCKER_CACHE_GHA: 1 + + - name: Collect build metrics + if: always() + run: | + make build-metrics + cat dist/metrics/metrics.txt >> "$GITHUB_STEP_SUMMARY" + env: + DOCKER_REGISTRY: ${{ github.event_name == 'pull_request' && format('{0}/dev', env.CONTAINER_REGISTRY) || env.CONTAINER_REGISTRY }} + DOCKER_TAG_VERSION: ${{ github.event_name == 'pull_request' && env.REL_VERSION || (github.ref == 'refs/heads/main' && 'latest' || env.REL_CHANNEL) }} + + - name: Upload build metrics + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: build-metrics-${{ github.job }} + path: dist/metrics/ diff --git a/.github/workflows/__publish-release.yaml b/.github/workflows/__publish-release.yaml new file mode 100644 index 0000000000..b2d1b0c444 --- /dev/null +++ b/.github/workflows/__publish-release.yaml @@ -0,0 +1,83 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: __publish-release + +on: + workflow_call: + +permissions: {} + +env: + RELEASE_PATH: ./release + +jobs: + publish-release: + name: Publish GitHub Release + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: write # Required for creating releases + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version-file: .python-version + + - name: Parse release version and set environment variables + run: python ./.github/scripts/get_release_version.py + + - name: Download release artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: rad_cli_* + merge-multiple: true + path: ${{ env.RELEASE_PATH }} + + - name: generate checksum files + run: | + cd "${RELEASE_PATH}" && for i in *; do sha256sum -b "$i" > "$i.sha256"; done && cd - + ls -l "${RELEASE_PATH}" + + - name: Create GitHub RC Release (pre-release and auto-generate release notes) + if: ${{ contains(env.REL_VERSION, 'rc') }} + run: | + gh release create "v${REL_VERSION}" \ + "${RELEASE_PATH}"/* \ + --title "Radius v${REL_VERSION}" \ + --generate-notes \ + --verify-tag \ + --prerelease + env: + GH_TOKEN: ${{ github.token }} + + - name: Create GitHub Official Release + if: ${{ !contains(env.REL_VERSION, 'rc') }} + run: | + gh release create "v${REL_VERSION}" \ + "${RELEASE_PATH}"/* \ + --title "Radius v${REL_VERSION}" \ + --notes-file "docs/release-notes/v${REL_VERSION}.md" \ + --verify-tag + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/build-main.yaml b/.github/workflows/build-main.yaml new file mode 100644 index 0000000000..5956ab8101 --- /dev/null +++ b/.github/workflows/build-main.yaml @@ -0,0 +1,120 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: build-main + +on: + workflow_dispatch: + push: + branches: + - main + +permissions: {} + +concurrency: + group: build-${{ github.ref }}-${{ github.sha }} + cancel-in-progress: true + +jobs: + changes: + name: Changes + uses: ./.github/workflows/__changes.yml + permissions: + contents: read # Required for actions/checkout + pull-requests: read # Required for change detection + + build-and-push-cli: + needs: [changes] + if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-cli.yaml + with: + only_changed: ${{ needs.changes.outputs.only_changed }} + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + issues: read # Required for publishing test results + checks: write # Required for publishing test results + pull-requests: write # Required for publishing test results + + build-check: + if: always() + name: Build Check + needs: [build-and-push-cli] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: {} + steps: + - name: Check build matrix results + if: ${{ needs.build-and-push-cli.result != 'success' && needs.build-and-push-cli.result != 'skipped' }} + run: | + echo "::error::Build and push CLI jobs failed or were cancelled" + exit 1 + + - name: Build matrix succeeded + if: ${{ needs.build-and-push-cli.result == 'success' || needs.build-and-push-cli.result == 'skipped' }} + run: | + echo "All build matrix jobs completed successfully" + exit 0 + + build-and-push-images: + needs: [changes] + if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-images.yaml + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + + build-and-push-helm-chart: + if: github.repository == 'radius-project/radius' && github.ref == 'refs/heads/main' && needs.changes.outputs.only_changed != 'true' + needs: [build-and-push-images, changes] + uses: ./.github/workflows/__build-helm-chart.yaml + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + + build-and-push-bicep-types: + needs: [changes] + if: github.repository == 'radius-project/radius' && github.ref == 'refs/heads/main' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-bicep-types.yaml + permissions: + contents: read # Required for actions/checkout + + build-summary: + if: always() + name: Build Summary + needs: [build-check, build-and-push-images, build-and-push-bicep-types] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read # Required for actions/checkout + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Check all build jobs + run: | + ./.github/scripts/build-summary.sh \ + "build-check:${BUILD_CHECK_RESULT}" \ + "build-and-push-images:${BUILD_AND_PUSH_IMAGES_RESULT}" \ + "build-and-push-bicep-types:${BUILD_AND_PUSH_BICEP_TYPES_RESULT}" + env: + BUILD_CHECK_RESULT: ${{ needs.build-check.result }} + BUILD_AND_PUSH_IMAGES_RESULT: ${{ needs.build-and-push-images.result }} + BUILD_AND_PUSH_BICEP_TYPES_RESULT: ${{ needs.build-and-push-bicep-types.result }} diff --git a/.github/workflows/build-release.yaml b/.github/workflows/build-release.yaml new file mode 100644 index 0000000000..5372a32640 --- /dev/null +++ b/.github/workflows/build-release.yaml @@ -0,0 +1,127 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: build-release + +on: + workflow_dispatch: + push: + tags: + - v* + +permissions: {} + +concurrency: + group: build-${{ github.ref }}-${{ github.sha }} + cancel-in-progress: true + +jobs: + changes: + name: Changes + uses: ./.github/workflows/__changes.yml + permissions: + contents: read # Required for actions/checkout + pull-requests: read # Required for change detection + + build-and-push-cli: + needs: [changes] + if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-cli.yaml + with: + only_changed: ${{ needs.changes.outputs.only_changed }} + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + issues: read # Required for publishing test results + checks: write # Required for publishing test results + pull-requests: write # Required for publishing test results + + build-check: + if: always() + name: Build Check + needs: [build-and-push-cli] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: {} + steps: + - name: Check build matrix results + if: ${{ needs.build-and-push-cli.result != 'success' && needs.build-and-push-cli.result != 'skipped' }} + run: | + echo "::error::Build and push CLI jobs failed or were cancelled" + exit 1 + + - name: Build matrix succeeded + if: ${{ needs.build-and-push-cli.result == 'success' || needs.build-and-push-cli.result == 'skipped' }} + run: | + echo "All build matrix jobs completed successfully" + exit 0 + + build-and-push-images: + needs: [changes] + if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-images.yaml + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + + build-and-push-helm-chart: + if: github.repository == 'radius-project/radius' && startsWith(github.ref, 'refs/tags/v') && needs.changes.outputs.only_changed != 'true' + needs: [build-and-push-images, changes] + uses: ./.github/workflows/__build-helm-chart.yaml + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + + build-and-push-bicep-types: + needs: [changes] + if: github.repository == 'radius-project/radius' && startsWith(github.ref, 'refs/tags/v') && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-bicep-types.yaml + permissions: + contents: read # Required for actions/checkout + + publish-release: + needs: [changes, build-and-push-cli] + if: github.repository == 'radius-project/radius' && startsWith(github.ref, 'refs/tags/v') && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__publish-release.yaml + permissions: + contents: write # Required for creating releases + + build-summary: + if: always() + name: Build Summary + needs: [build-check, build-and-push-images, build-and-push-bicep-types] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read # Required for actions/checkout + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Check all build jobs + run: | + ./.github/scripts/build-summary.sh \ + "build-check:${BUILD_CHECK_RESULT}" \ + "build-and-push-images:${BUILD_AND_PUSH_IMAGES_RESULT}" \ + "build-and-push-bicep-types:${BUILD_AND_PUSH_BICEP_TYPES_RESULT}" + env: + BUILD_CHECK_RESULT: ${{ needs.build-check.result }} + BUILD_AND_PUSH_IMAGES_RESULT: ${{ needs.build-and-push-images.result }} + BUILD_AND_PUSH_BICEP_TYPES_RESULT: ${{ needs.build-and-push-bicep-types.result }} diff --git a/.github/workflows/build-validation.yaml b/.github/workflows/build-validation.yaml new file mode 100644 index 0000000000..54b5e789aa --- /dev/null +++ b/.github/workflows/build-validation.yaml @@ -0,0 +1,111 @@ +# ------------------------------------------------------------ +# Copyright 2026 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ------------------------------------------------------------ + +# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json +--- +name: build-validation + +on: + workflow_dispatch: + push: + branches: + - release/* + pull_request: + branches: + - main + - features/* + - release/* + merge_group: + branches: + - main + +permissions: {} + +concurrency: + group: build-${{ github.ref }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +jobs: + changes: + name: Changes + uses: ./.github/workflows/__changes.yml + permissions: + contents: read # Required for actions/checkout + pull-requests: read # Required for change detection + + build-and-push-cli: + needs: [changes] + if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-cli.yaml + with: + only_changed: ${{ needs.changes.outputs.only_changed }} + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + issues: read # Required for publishing test results + checks: write # Required for publishing test results + pull-requests: write # Required for publishing test results + + build-check: + if: always() + name: Build Check + needs: [build-and-push-cli] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: {} + steps: + - name: Check build matrix results + if: ${{ needs.build-and-push-cli.result != 'success' && needs.build-and-push-cli.result != 'skipped' }} + run: | + echo "::error::Build and push CLI jobs failed or were cancelled" + exit 1 + + - name: Build matrix succeeded + if: ${{ needs.build-and-push-cli.result == 'success' || needs.build-and-push-cli.result == 'skipped' }} + run: | + echo "All build matrix jobs completed successfully" + exit 0 + + build-and-push-images: + needs: [changes] + if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' + uses: ./.github/workflows/__build-images.yaml + permissions: + packages: write # Required for uploading the package + contents: read # Required for actions/checkout + + build-summary: + if: always() + name: Build Summary + needs: [build-check, build-and-push-images] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read # Required for actions/checkout + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Check all build jobs + run: | + ./.github/scripts/build-summary.sh \ + "build-check:${BUILD_CHECK_RESULT}" \ + "build-and-push-images:${BUILD_AND_PUSH_IMAGES_RESULT}" + env: + BUILD_CHECK_RESULT: ${{ needs.build-check.result }} + BUILD_AND_PUSH_IMAGES_RESULT: ${{ needs.build-and-push-images.result }} diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml deleted file mode 100644 index e3521e2247..0000000000 --- a/.github/workflows/build.yaml +++ /dev/null @@ -1,547 +0,0 @@ -# ------------------------------------------------------------ -# Copyright 2023 The Radius Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ------------------------------------------------------------ - -# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json ---- -name: Build and Test - -on: - # Enable manual trigger - workflow_dispatch: - push: - branches: - - main - - release/* - tags: - - v* - pull_request: - branches: - - main - - features/* - - release/* - # Revalidate the combined changes when a PR is queued in the merge queue. - # Scoped to main only so the release branch flow is never affected. - merge_group: - branches: - - main - -permissions: {} - -concurrency: - # Cancel the previously triggered build for only PR build. - group: build-${{ github.ref }}-${{ github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -env: - # GitHub Actor for pushing images to GHCR - GHCR_ACTOR: rad-ci-bot - - # Container registry url for GitHub container registry. - CONTAINER_REGISTRY: ghcr.io/radius-project - - # Local file path to the release binaries. - RELEASE_PATH: ./release - - # URL to get source code for building the image - IMAGE_SRC: https://github.com/radius-project/radius - -jobs: - changes: - name: Changes - uses: ./.github/workflows/__changes.yml - permissions: - contents: read - pull-requests: read - - build-and-push-cli: - name: Build ${{ matrix.target_os }}_${{ matrix.target_arch }} binaries - needs: [changes] - if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' - runs-on: ubuntu-24.04 - timeout-minutes: 30 - permissions: - packages: write # Required for uploading the package - contents: read # Required for actions/checkout - issues: read # Required for EnricoMi/publish-unit-test-result-action - checks: write # Required for EnricoMi/publish-unit-test-result-action - pull-requests: write # Required for EnricoMi/publish-unit-test-result-action - env: - GOOS: ${{ matrix.target_os }} - GOARCH: ${{ matrix.target_arch }} - GOPROXY: https://proxy.golang.org - strategy: - fail-fast: false - matrix: - include: - - target_os: linux - target_arch: amd64 - - target_os: linux - target_arch: arm64 - - target_os: linux - target_arch: arm - - target_os: windows - target_arch: amd64 - - target_os: windows - target_arch: arm64 - - target_os: darwin - target_arch: amd64 - - target_os: darwin - target_arch: arm64 - steps: - - name: Checkout - if: needs.changes.outputs.only_changed != 'true' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Setup Go - if: needs.changes.outputs.only_changed != 'true' - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: go.mod - cache-dependency-path: go.sum - cache: false - - - name: Setup Python - if: needs.changes.outputs.only_changed != 'true' - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version-file: .python-version - - - name: Parse release version and set environment variables - if: needs.changes.outputs.only_changed != 'true' - run: python ./.github/scripts/get_release_version.py - - - name: Make build - if: needs.changes.outputs.only_changed != 'true' - run: make build - - - name: Copy cli binaries to release (unix-like) - if: matrix.target_os != 'windows' && needs.changes.outputs.only_changed != 'true' - run: | - mkdir -p "${RELEASE_PATH}" - cp "./dist/${GOOS}_${GOARCH}/release/rad" "${RELEASE_PATH}/rad_${GOOS}_${GOARCH}" - - - name: Copy cli binaries to release (windows) - if: matrix.target_os == 'windows' && needs.changes.outputs.only_changed != 'true' - run: | - mkdir -p "${RELEASE_PATH}" - cp "./dist/${GOOS}_${GOARCH}/release/rad.exe" "${RELEASE_PATH}/rad_${GOOS}_${GOARCH}.exe" - - - name: Upload CLI binary - if: needs.changes.outputs.only_changed != 'true' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: rad_cli_${{ matrix.target_os}}_${{ matrix.target_arch}} - path: ${{ env.RELEASE_PATH }} - - - name: Login to GitHub Container Registry - if: needs.changes.outputs.only_changed != 'true' - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ github.token }} - - - name: Install oras - if: needs.changes.outputs.only_changed != 'true' - # Version pinned in build/tools.yaml (ORAS_VERSION). - run: make install-oras - - - name: Push latest rad cli binary to GHCR (unix-like) - if: github.ref == 'refs/heads/main' && matrix.target_os != 'windows' && needs.changes.outputs.only_changed != 'true' - run: | - cp "./dist/${GOOS}_${GOARCH}/release/rad" ./rad - oras push "${CONTAINER_REGISTRY}/rad/${GOOS}-${GOARCH}:latest" ./rad --annotation "org.opencontainers.image.source=${IMAGE_SRC}" - - - name: Copy cli binaries to release (windows) - if: github.ref == 'refs/heads/main' && matrix.target_os == 'windows' && needs.changes.outputs.only_changed != 'true' - run: | - cp "./dist/${GOOS}_${GOARCH}/release/rad.exe" ./rad.exe - oras push "${CONTAINER_REGISTRY}/rad/${GOOS}-${GOARCH}:latest" ./rad.exe --annotation "org.opencontainers.image.source=${IMAGE_SRC}" - - - name: Skip - if: needs.changes.outputs.only_changed == 'true' - run: exit 0 - - # Returns success if all matrix jobs in build-and-push-cli are successful - otherwise, it returns a failure. - # Use this as a PR status check for GitHub Policy Service instead of individual matrix entry checks. - build-check: - if: always() - name: Build Check - needs: [build-and-push-cli] - runs-on: ubuntu-24.04 - timeout-minutes: 5 - steps: - - name: Check build matrix results - if: ${{ needs.build-and-push-cli.result != 'success' && needs.build-and-push-cli.result != 'skipped' }} - run: | - echo "::error::Build and push CLI jobs failed or were cancelled" - exit 1 - - - name: Build matrix succeeded - if: ${{ needs.build-and-push-cli.result == 'success' || needs.build-and-push-cli.result == 'skipped' }} - run: | - echo "All build matrix jobs completed successfully" - exit 0 - - build-and-push-images: - name: Build and publish container images - needs: [changes] - if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' - runs-on: ubuntu-24.04 - timeout-minutes: 30 - permissions: - packages: write # Required for uploading the package - contents: read - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version-file: .python-version - - - name: Parse release version and set environment variables - run: python ./.github/scripts/get_release_version.py - - - name: Setup Go - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: go.mod - cache-dependency-path: go.sum - cache: false - - - name: Login to GitHub Container Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ github.token }} - - - name: Setup QEMU - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 - with: - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - - name: Build container images (PR) - if: github.event_name == 'pull_request' - run: | - make docker-build - env: - DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }}/dev - DOCKER_TAG_VERSION: ${{ env.REL_VERSION }} - DOCKER_CACHE_GHA: 1 - - - name: Save container images to artifacts (PR) - if: github.event_name == 'pull_request' - run: | - make docker-save-images - env: - DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }}/dev - DOCKER_TAG_VERSION: ${{ env.REL_VERSION }} - - - name: Upload container image artifacts (PR) - if: github.event_name == 'pull_request' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: container-images-${{ env.REL_VERSION }} - path: ./dist/images/ - retention-days: 1 - - - name: Push container images (latest) - run: | - make docker-multi-arch-push - if: (github.ref == 'refs/heads/main') # push image to latest on merge to main - env: - DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }} - DOCKER_TAG_VERSION: latest - DOCKER_CACHE_GHA: 1 - - - name: Push container images (release) - run: | - make docker-multi-arch-push - if: startsWith(github.ref, 'refs/tags/v') # push image on tag - env: - DOCKER_REGISTRY: ${{ env.CONTAINER_REGISTRY }} - DOCKER_TAG_VERSION: ${{ env.REL_CHANNEL }} - DOCKER_CACHE_GHA: 1 - - - name: Collect build metrics - if: always() - run: | - make build-metrics - cat dist/metrics/metrics.txt >> $GITHUB_STEP_SUMMARY - env: - DOCKER_REGISTRY: ${{ github.event_name == 'pull_request' && format('{0}/dev', env.CONTAINER_REGISTRY) || env.CONTAINER_REGISTRY }} - DOCKER_TAG_VERSION: ${{ github.event_name == 'pull_request' && env.REL_VERSION || (github.ref == 'refs/heads/main' && 'latest' || env.REL_CHANNEL) }} - - - name: Upload build metrics - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: build-metrics-${{ github.job }} - path: dist/metrics/ - - build-and-push-helm-chart: - name: Helm chart build - # Don't push on PR, agent will not have permission. - if: github.repository == 'radius-project/radius' && ((startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')) && needs.changes.outputs.only_changed != 'true' - needs: [build-and-push-images, changes] - runs-on: ubuntu-24.04 - timeout-minutes: 5 - permissions: - packages: write # Required for uploading the package - contents: read - env: - ARTIFACT_DIR: ./dist/Charts - HELM_PACKAGE_DIR: helm - HELM_CHARTS_DIR: deploy/Chart - OCI_REGISTRY: ghcr.io - # We only push the chart on pushes to main or to a tag. The versioning logic will select the right - # version for us. - OCI_REPOSITORY: radius-project/helm-chart - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Install helm - # Version pinned in build/tools.yaml (HELM_VERSION). - run: make install-helm - - - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version-file: .python-version - - - name: Parse release version and set environment variables - run: python ./.github/scripts/get_release_version.py - - - name: Run Helm linter - run: | - helm lint "${HELM_CHARTS_DIR}" - - - name: Package Helm chart - run: | - mkdir -p "${ARTIFACT_DIR}/${HELM_PACKAGE_DIR}" - helm package "${HELM_CHARTS_DIR}" --version "${CHART_VERSION}" --app-version "${REL_VERSION}" --destination "${ARTIFACT_DIR}/${HELM_PACKAGE_DIR}" - - - name: Push helm chart to GHCR - env: - GH_TOKEN: ${{ github.token }} - GH_ACTOR: ${{ github.actor }} - run: | - echo "${GH_TOKEN}" | helm registry login -u "${GH_ACTOR}" --password-stdin "${OCI_REGISTRY}" - helm push "${ARTIFACT_DIR}/${HELM_PACKAGE_DIR}/radius-${CHART_VERSION}.tgz" "oci://${OCI_REGISTRY}/${OCI_REPOSITORY}" - - build-and-push-bicep-types: - name: Dispatch Bicep Types publish - runs-on: ubuntu-24.04 - timeout-minutes: 15 - needs: [changes] - if: github.repository == 'radius-project/radius' && needs.changes.outputs.only_changed != 'true' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - environment: publish-bicep - permissions: - contents: read # Required for actions/checkout - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version-file: .python-version - - - name: Parse release version and set environment variables - run: python ./.github/scripts/get_release_version.py - - - name: Get App Token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - id: get-token - with: - client-id: ${{ secrets.RADIUS_PUBLISHER_BOT_CLIENT_ID }} - private-key: ${{ secrets.RADIUS_PUBLISHER_BOT_PRIVATE_KEY }} - permission-metadata: read - permission-actions: read - permission-contents: write - owner: azure-octo - repositories: | - radius-publisher - - - name: Capture dispatch start time - id: dispatch-start - shell: bash - run: | - echo "started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" - - - name: Repository Dispatch - id: repository-dispatch - uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 - with: - token: ${{ steps.get-token.outputs.token }} - repository: azure-octo/radius-publisher - event-type: bicep-types - client-payload: |- - { - "source_repository": "${{ github.repository }}", - "source_ref": "${{ github.ref }}", - "source_sha": "${{ github.sha }}", - "rel_channel": "${{ env.REL_CHANNEL }}", - "registry_target": "radius" - } - - - name: Monitor remote workflow - id: monitor-remote-workflow - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - github-token: ${{ steps.get-token.outputs.token }} - script: | - const { default: script } = await import(`${process.env.GITHUB_WORKSPACE}/.github/scripts/monitor-remote-workflow.mjs`) - await script({context, github, core}) - env: - INPUT_OWNER: azure-octo - INPUT_REPO: radius-publisher - INPUT_WORKFLOW_FILE: publish-bicep-types.yml - INPUT_DISPATCH_STARTED_AT: ${{ steps.dispatch-start.outputs.started_at }} - INPUT_MAX_WAIT_SECONDS: "600" - INPUT_POLL_INTERVAL_SECONDS: "15" - - - name: Show failed logs - if: failure() && steps.monitor-remote-workflow.outputs.run_id != '' - shell: bash - env: - GH_TOKEN: ${{ steps.get-token.outputs.token }} - RUN_ID: ${{ steps.monitor-remote-workflow.outputs.run_id }} - run: | - gh run view "${RUN_ID}" --repo azure-octo/radius-publisher --log-failed || true - - publish-release: - name: Publish GitHub Release - needs: [changes, build-and-push-cli] - runs-on: ubuntu-24.04 - timeout-minutes: 5 - if: github.repository == 'radius-project/radius' && startsWith(github.ref, 'refs/tags/v') && needs.changes.outputs.only_changed != 'true' - permissions: - contents: write # Required for creating releases - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version-file: .python-version - - - name: Parse release version and set environment variables - run: python ./.github/scripts/get_release_version.py - - - name: Download release artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - pattern: rad_cli_* - merge-multiple: true - path: ${{ env.RELEASE_PATH }} - - - name: generate checksum files - run: | - cd "${RELEASE_PATH}" && for i in *; do sha256sum -b "$i" > "$i.sha256"; done && cd - - ls -l "${RELEASE_PATH}" - - - name: Create GitHub RC Release (pre-release and auto-generate release notes) - if: ${{ contains(env.REL_VERSION, 'rc') }} - run: | - gh release create "v${REL_VERSION}" \ - "${RELEASE_PATH}"/* \ - --title "Radius v${REL_VERSION}" \ - --generate-notes \ - --verify-tag \ - --prerelease - env: - GH_TOKEN: ${{ github.token }} - - - name: Create GitHub Official Release - if: ${{ !contains(env.REL_VERSION, 'rc') }} - run: | - gh release create "v${REL_VERSION}" \ - "${RELEASE_PATH}"/* \ - --title "Radius v${REL_VERSION}" \ - --notes-file "docs/release-notes/v${REL_VERSION}.md" \ - --verify-tag - env: - GH_TOKEN: ${{ github.token }} - - # Comprehensive build summary that checks all critical build jobs. - # Use this as the required status check in PR branch protection rules. - build-summary: - if: always() - name: Build Summary - needs: [build-check, build-and-push-images, build-and-push-bicep-types] - runs-on: ubuntu-24.04 - timeout-minutes: 5 - permissions: {} - steps: - - name: Check all build jobs - run: | - results=( - "build-check:${BUILD_CHECK_RESULT}" - "build-and-push-images:${BUILD_AND_PUSH_IMAGES_RESULT}" - "build-and-push-bicep-types:${BUILD_AND_PUSH_BICEP_TYPES_RESULT}" - ) - - echo "## Build Results Summary" >> "${GITHUB_STEP_SUMMARY}" - echo "" >> "${GITHUB_STEP_SUMMARY}" - echo "| Job | Status |" >> "${GITHUB_STEP_SUMMARY}" - echo "|-----|--------|" >> "${GITHUB_STEP_SUMMARY}" - - failed=0 - for result in "${results[@]}"; do - job="${result%%:*}" - status="${result##*:}" - - if [ "$status" = "success" ] || [ "$status" = "skipped" ]; then - echo "| $job | ✅ $status |" >> "${GITHUB_STEP_SUMMARY}" - else - echo "| $job | ❌ $status |" >> "${GITHUB_STEP_SUMMARY}" - failed=1 - fi - done - - echo "" >> "${GITHUB_STEP_SUMMARY}" - - if [ $failed -eq 1 ]; then - echo "::error::One or more build jobs failed or were cancelled" - echo "**Result: ❌ BUILD FAILED**" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - else - echo "**Result: ✅ ALL BUILDS PASSED**" >> "${GITHUB_STEP_SUMMARY}" - echo "All build jobs completed successfully" - fi - env: - BUILD_CHECK_RESULT: ${{ needs.build-check.result }} - BUILD_AND_PUSH_IMAGES_RESULT: ${{ needs.build-and-push-images.result }} - BUILD_AND_PUSH_BICEP_TYPES_RESULT: ${{ needs.build-and-push-bicep-types.result }} diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 314035e145..ca91eb3aa9 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -22,8 +22,9 @@ # test Radius without relying on the dev container or its post-create script. # # The steps below intentionally mirror the tooling installed by the contributor -# dev container (.devcontainer/) and by the CI workflows (build.yaml and -# functional-test-noncloud.yaml). Language runtime versions are read from the +# dev container (.devcontainer/) and by the CI workflows (build-validation.yaml, +# build-main.yaml, build-release.yaml, and functional-test-noncloud.yaml). +# Language runtime versions are read from the # repo's version files (go.mod, .node-version, .python-version, .terraform-version) # so a version bump flows here automatically; CLI tool versions and checksums are # pinned in build/tools.yaml and installed via the shared `make install-` diff --git a/.github/workflows/update-resource-types.yaml b/.github/workflows/update-resource-types.yaml index 1e594256a7..81e40cd4d6 100644 --- a/.github/workflows/update-resource-types.yaml +++ b/.github/workflows/update-resource-types.yaml @@ -48,7 +48,7 @@ name: Update Resource Types # updates stay cumulative without the branch ever having to be rebased. # # Merging the resulting PR triggers the existing `build-and-push-bicep-types` -# job in build.yaml, which dispatches to azure-octo/radius-publisher to +# job in build-main.yaml, which dispatches to azure-octo/radius-publisher to # republish `radius:latest` with the refreshed contrib types. # # See the design notes: @@ -378,4 +378,4 @@ jobs: Each affected entry is pinned to an immutable commit SHA in `deploy/manifest/defaults.yaml` (`resourceTypes[]` / `recipePacks[]`). - Merging this PR will republish `br:biceptypes.azurecr.io/radius:latest` via the existing `build-and-push-bicep-types` job in `build.yaml`. + Merging this PR will republish `br:biceptypes.azurecr.io/radius:latest` via the existing `build-and-push-bicep-types` job in `build-main.yaml`. diff --git a/build/test.mk b/build/test.mk index b3133ef6e3..1a5dae783a 100644 --- a/build/test.mk +++ b/build/test.mk @@ -53,7 +53,7 @@ GOTEST_OPTS ?= GOTEST_TOOL ?= go tool gotestsum $(GOTESTSUM_OPTS) -- .PHONY: test -test: test-get-envtools test-helm test-manage-radius-installation test-release-parity-manifest test-changelog-range ## Runs unit tests, excluding kubernetes controller tests +test: test-get-envtools test-helm test-manage-radius-installation test-release-parity-manifest test-changelog-range test-build-summary ## Runs unit tests, excluding kubernetes controller tests KUBEBUILDER_ASSETS="$(shell $(ENV_SETUP) use -p path ${K8S_VERSION} --arch amd64)" CGO_ENABLED=1 $(GOTEST_TOOL) ./pkg/... ./test/validation/... $(GOTEST_OPTS) .PHONY: test-manage-radius-installation @@ -68,6 +68,10 @@ test-release-parity-manifest: ## Tests release parity manifest collection test-changelog-range: ## Tests changelog channel boundary resolution @bash ./.github/scripts/changelog-range_test.sh +.PHONY: test-build-summary +test-build-summary: ## Tests the build job summary rendering shared by the build workflows + @bash ./.github/scripts/build-summary_test.sh + .PHONY: test-compile test-compile: test-get-envtools ## Compiles all tests without running them @echo "$(ARROW) Compiling unit tests..." diff --git a/docs/contributing/contributing-releases/README.md b/docs/contributing/contributing-releases/README.md index 1bcc217def..c5b44591b5 100644 --- a/docs/contributing/contributing-releases/README.md +++ b/docs/contributing/contributing-releases/README.md @@ -62,7 +62,7 @@ Two GitHub Actions workflows drive the release process. **No one manually create > - Add the new release version at the top of the `supported` list in `versions.yaml`. > - Change only one version per PR. > - If more than one new untagged version is present in `supported`, `release.yaml` fails rather than guessing which one to release. -2. **[Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml)** (`build.yaml`): Triggered by `v*` tag pushes (created by `release.yaml` above). This workflow: +2. **[Release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml)** (`build-release.yaml`): Triggered by `v*` tag pushes (created by `release.yaml` above). This workflow: - Builds CLI binaries and container images - Dispatches Bicep types publishing - Creates the GitHub Release (auto-generated notes for RCs, or from `docs/release-notes/` for final and patch releases) @@ -73,8 +73,8 @@ The automated flow after merging a `versions.yaml` change: Merge versions.yaml change (to main or release/* branch) → release.yaml detects new version in versions.yaml → release.yaml creates git tag + release branch (if needed) - → tag push triggers build.yaml - → build.yaml publishes artifacts + creates GitHub Release + → tag push triggers build-release.yaml + → build-release.yaml publishes artifacts + creates GitHub Release ``` #### When does tag creation happen? @@ -169,13 +169,13 @@ After approval, merge the PR to `main`. After merging, the [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow automatically runs because `versions.yaml` changed on `main`. -- **First RC**: The workflow creates the `release/X.Y` branch from `main` and pushes the `vX.Y.Z-rcN` tag. The tag push then triggers the [Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml) workflow. No manual tag creation is needed. Verify the release using the checklist below. +- **First RC**: The workflow creates the `release/X.Y` branch from `main` and pushes the `vX.Y.Z-rcN` tag. The tag push then triggers the [release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml) workflow. No manual tag creation is needed. Verify the release using the checklist below. - **Subsequent RCs**: The workflow detects that the release branch already exists and **skips tag creation**. This is expected — the tag will be created when the cherry-pick lands on the release branch in [Step 6](#step-6-cherry-pick-additional-changes-subsequent-rcs-only). Skip ahead to Step 6 for now and return to verify after completing it. Monitor and verify: 1. The [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow completes successfully. For the first RC, confirm it created the `release/X.Y` [branch](https://github.com/radius-project/radius/branches) and the `vX.Y.Z-rcN` [tag](https://github.com/radius-project/radius/tags). -2. The [Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml) workflow (triggered by the tag push) completes successfully. This workflow also dispatches Bicep types publishing automatically. +2. The [release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml) workflow (triggered by the tag push) completes successfully. This workflow also dispatches Bicep types publishing automatically. 3. An RC release marked as pre-release appears on [GitHub Releases](https://github.com/radius-project/radius/releases). ### Step 6: Cherry-pick additional changes (subsequent RCs only) @@ -296,12 +296,12 @@ After approval, merge the PR. ### Step 6: Verify the automated release -After the cherry-pick PR is merged to the `release/X.Y` branch, the [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow automatically runs because `versions.yaml` changed on a `release/*` branch. It reads the final version from `versions.yaml`, creates and pushes the `vX.Y.Z` tag, and the tag push triggers the [Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml) workflow. No manual tag creation is needed. +After the cherry-pick PR is merged to the `release/X.Y` branch, the [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow automatically runs because `versions.yaml` changed on a `release/*` branch. It reads the final version from `versions.yaml`, creates and pushes the `vX.Y.Z` tag, and the tag push triggers the [release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml) workflow. No manual tag creation is needed. Monitor and verify: 1. The [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow completes successfully and creates the `vX.Y.Z` [tag](https://github.com/radius-project/radius/tags). -2. The [Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml) workflow (triggered by the tag push) completes successfully. Allow up to ~20 minutes for release assets to be published. +2. The [release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml) workflow (triggered by the tag push) completes successfully. Allow up to ~20 minutes for release assets to be published. 3. A final release (not pre-release) appears on [GitHub Releases](https://github.com/radius-project/radius/releases). ### Step 7: Publish docs and samples @@ -379,12 +379,12 @@ After approval, merge the PR. ### Step 5: Verify the automated release -After the cherry-pick PR is merged to the `release/X.Y` branch, the [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow automatically runs because `versions.yaml` changed on a `release/*` branch. It reads the patch version from `versions.yaml`, creates and pushes the `vX.Y.Z` tag, and the tag push triggers the [Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml) workflow. No manual tag creation is needed. +After the cherry-pick PR is merged to the `release/X.Y` branch, the [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow automatically runs because `versions.yaml` changed on a `release/*` branch. It reads the patch version from `versions.yaml`, creates and pushes the `vX.Y.Z` tag, and the tag push triggers the [release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml) workflow. No manual tag creation is needed. Monitor and verify: 1. The [Release Radius](https://github.com/radius-project/radius/actions/workflows/release.yaml) workflow completes successfully and creates the `vX.Y.Z` [tag](https://github.com/radius-project/radius/tags). -2. The [Build and Test](https://github.com/radius-project/radius/actions/workflows/build.yaml) workflow (triggered by the tag push) completes successfully. Allow up to ~20 minutes for release assets to be published. +2. The [release build](https://github.com/radius-project/radius/actions/workflows/build-release.yaml) workflow (triggered by the tag push) completes successfully. Allow up to ~20 minutes for release assets to be published. 3. A patch release appears on [GitHub Releases](https://github.com/radius-project/radius/releases). ### Step 6: Run validation workflows