Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/scripts/build-summary.sh
Original file line number Diff line number Diff line change
@@ -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 "<job>:<result>" ["<job>:<result>" ...]
#
# 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 \"<job>:<result>\" [\"<job>:<result>\" ...]" >&2
}

main() {
if [[ $# -eq 0 ]]; then
echo "Error: at least one \"<job>:<result>\" 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 \"<job>:<result>\"" >&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 "$@"
169 changes: 169 additions & 0 deletions .github/scripts/build-summary_test.sh
Original file line number Diff line number Diff line change
@@ -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 "$@"
106 changes: 106 additions & 0 deletions .github/workflows/__build-bicep-types.yaml
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading