From b95cfcd6c2fb254ca9c5a0c08a165dc2945b6880 Mon Sep 17 00:00:00 2001 From: Nick Dimiduk Date: Mon, 2 Feb 2026 10:05:44 +0100 Subject: [PATCH 1/2] YETUS-1260. Add GitHub Actions Job Summary support --- .../precommit/robots/githubactions.html.md | 19 +++ .../src/main/shell/robots.d/githubactions.sh | 134 +++++++++++++++++- .../test/shell/githubactions_finalreport.bats | 131 +++++++++++++++++ 3 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 precommit/src/test/shell/githubactions_finalreport.bats diff --git a/asf-site-src/source/documentation/in-progress/precommit/robots/githubactions.html.md b/asf-site-src/source/documentation/in-progress/precommit/robots/githubactions.html.md index 698cbb554..346f6dce0 100644 --- a/asf-site-src/source/documentation/in-progress/precommit/robots/githubactions.html.md +++ b/asf-site-src/source/documentation/in-progress/precommit/robots/githubactions.html.md @@ -24,6 +24,8 @@ GitHub Action support is available in two different ways. There are some settin * Annotations will be used to mark problems in the files for those plug-ins that support this feature and if `--linecomments` has `github` as a configured bug system (the default). * Statuses will be added if the GitHub Token gives permission. +* Job Summary will be written with a Markdown-formatted report of the test results, visible directly on the +workflow run page without downloading artifacts. ## Workflow Action @@ -108,6 +110,23 @@ TRIGGER: ${GITHUB_ACTIONS}=True GitHub Actions support has only been tested on the ubuntu-latest image. It automatically configures `--patch-dir` to be `${GITHUB_WORKSAPCE}/yetus` if not previously set. +## Job Summary + +When running under GitHub Actions, Apache Yetus automatically writes a summary of the test results to the +[Job Summary](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/). This provides +immediate visibility of pass/fail status and details directly on the workflow run page, without needing to +download artifacts or parse log files. + +The Job Summary includes: + +* Overall pass/fail status with vote counts +* Vote table showing each subsystem's result, runtime, and comments +* Failed tests section (if any tests failed) +* Links to log files (if artifact URLs are available) + +This feature requires no configuration and is automatically enabled when the `GITHUB_STEP_SUMMARY` environment +variable is present (which GitHub Actions sets automatically). + See also: * Apache Yetus' [workflow action source](https://github.com/apache/yetus-test-patch-action) for lower level details on the workflow action implementation. diff --git a/precommit/src/main/shell/robots.d/githubactions.sh b/precommit/src/main/shell/robots.d/githubactions.sh index 461cb362d..75248ff62 100755 --- a/precommit/src/main/shell/robots.d/githubactions.sh +++ b/precommit/src/main/shell/robots.d/githubactions.sh @@ -88,4 +88,136 @@ function githubactions_set_plugin_defaults function githubactions_cleanup_and_exit { echo "::endgroup::" -} \ No newline at end of file +} + +## @description Write a summary report to GitHub Actions Job Summary +## @audience private +## @stability evolving +## @replaceable no +function githubactions_finalreport +{ + declare -i i=0 + declare ourstring + declare vote + declare subs + declare ela + declare calctime + declare logfile + declare comment + declare url + declare emoji + declare loglink + + if [[ -z "${GITHUB_STEP_SUMMARY}" ]]; then + return 0 + fi + + if [[ ! -w "${GITHUB_STEP_SUMMARY}" ]]; then + yetus_error "WARNING: GITHUB_STEP_SUMMARY (${GITHUB_STEP_SUMMARY}) is not writable" + return 0 + fi + + big_console_header "Writing GitHub Actions Job Summary" + + url=$(get_artifact_url) + + { + if [[ ${RESULT} == 0 ]]; then + printf '## :confetti_ball: +1 overall\n\n' + else + printf '## :broken_heart: -1 overall\n\n' + fi + + i=0 + until [[ ${i} -ge ${#TP_HEADER[@]} ]]; do + printf '%s\n\n' "${TP_HEADER[i]}" + ((i=i+1)) + done + + printf '| Vote | Subsystem | Runtime | Logfile | Comment |\n' + printf '|:----:|----------:|--------:|:-------:|:--------|\n' + + i=0 + until [[ ${i} -ge ${#TP_VOTE_TABLE[@]} ]]; do + ourstring=$(echo "${TP_VOTE_TABLE[i]}" | tr -s ' ') + vote=$(echo "${ourstring}" | cut -f2 -d\| | tr -d ' ') + subs=$(echo "${ourstring}" | cut -f3 -d\|) + ela=$(echo "${ourstring}" | cut -f4 -d\|) + calctime=$(clock_display "${ela}") + logfile=$(echo "${ourstring}" | cut -f5 -d\| | tr -d ' ') + comment=$(echo "${ourstring}" | cut -f6 -d\|) + + if [[ "${vote}" = "H" ]]; then + printf '| | | | | _%s_ |\n' "${comment}" + ((i=i+1)) + continue + fi + + # Honor GITHUB_USE_EMOJI_VOTE setting + if [[ ${GITHUB_USE_EMOJI_VOTE} == true ]]; then + case ${vote} in + 1|"+1") + emoji="+1 :green_heart:" + ;; + -1) + emoji="-1 :x:" + ;; + 0) + emoji="+0 :ok:" + ;; + -0) + emoji="-0 :warning:" + ;; + *) + emoji=${vote} + ;; + esac + else + emoji="${vote}" + fi + + # Format logfile as link if URL is available + if [[ -n "${logfile}" ]]; then + if [[ -n "${url}" ]]; then + loglink=$(echo "${logfile}" | "${SED}" -e "s,@@BASE@@,${url},g") + loglink="[${logfile/@@BASE@@\//}](${loglink})" + else + loglink="${logfile/@@BASE@@\//}" + fi + else + loglink="" + fi + + printf '| %s | %s | %s | %s | %s |\n' \ + "${emoji}" \ + "${subs}" \ + "${calctime}" \ + "${loglink}" \ + "${comment}" + + ((i=i+1)) + done + + if [[ ${#TP_TEST_TABLE[@]} -gt 0 ]]; then + printf '\n### Failed Tests\n\n' + printf '| Reason | Tests |\n' + printf '|-------:|:------|\n' + i=0 + until [[ ${i} -ge ${#TP_TEST_TABLE[@]} ]]; do + echo "${TP_TEST_TABLE[i]}" + ((i=i+1)) + done + fi + + printf '\n### Subsystem Report\n\n' + printf '| Subsystem | Report/Notes |\n' + printf '|----------:|:-------------|\n' + + i=0 + until [[ ${i} -ge ${#TP_FOOTER_TABLE[@]} ]]; do + comment=$(echo "${TP_FOOTER_TABLE[i]}" | "${SED}" -e "s,@@BASE@@,${url},g") + printf '%s\n' "${comment}" + ((i=i+1)) + done + } >> "${GITHUB_STEP_SUMMARY}" +} diff --git a/precommit/src/test/shell/githubactions_finalreport.bats b/precommit/src/test/shell/githubactions_finalreport.bats new file mode 100644 index 000000000..df3ee079e --- /dev/null +++ b/precommit/src/test/shell/githubactions_finalreport.bats @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +load functions_test_helper + +setup_gha() { + # Source the githubactions robot + # shellcheck disable=SC1090 + . "${BATS_TEST_DIRNAME}/../../main/shell/robots.d/githubactions.sh" + + # Mock required functions + big_console_header() { :; } + clock_display() { echo "$1s"; } + get_artifact_url() { echo ""; } + yetus_error() { echo "$*" >&2; } + SED="sed" + + # Set up step summary file + GITHUB_STEP_SUMMARY="${TMP}/step_summary.md" + touch "${GITHUB_STEP_SUMMARY}" + export GITHUB_STEP_SUMMARY + + # Initialize arrays and settings + TP_VOTE_TABLE=() + TP_TEST_TABLE=() + TP_HEADER=() + TP_FOOTER_TABLE=() + VERSION="0.0.0-test" + GITHUB_USE_EMOJI_VOTE=false +} + +@test "githubactions_finalreport (no GITHUB_STEP_SUMMARY)" { + setup_gha + unset GITHUB_STEP_SUMMARY + run githubactions_finalreport + [ "${status}" -eq 0 ] +} + +@test "githubactions_finalreport (GITHUB_STEP_SUMMARY not writable)" { + setup_gha + GITHUB_STEP_SUMMARY="/nonexistent/path/summary.md" + run githubactions_finalreport + [ "${status}" -eq 0 ] +} + +@test "githubactions_finalreport (success result)" { + setup_gha + RESULT=0 + TP_VOTE_TABLE=("|+1| compile |60||passed|") + run githubactions_finalreport + [ "${status}" -eq 0 ] + grep -q ":confetti_ball:" "${GITHUB_STEP_SUMMARY}" + grep -q "+1 overall" "${GITHUB_STEP_SUMMARY}" + grep -q "compile" "${GITHUB_STEP_SUMMARY}" +} + +@test "githubactions_finalreport (failure result)" { + setup_gha + RESULT=1 + TP_VOTE_TABLE=("|-1| unit |120||tests failed|") + run githubactions_finalreport + [ "${status}" -eq 0 ] + grep -q ":broken_heart:" "${GITHUB_STEP_SUMMARY}" + grep -q -- "-1 overall" "${GITHUB_STEP_SUMMARY}" + grep -q "unit" "${GITHUB_STEP_SUMMARY}" +} + +@test "githubactions_finalreport (with headers)" { + setup_gha + RESULT=0 + TP_HEADER=("Build completed successfully") + TP_VOTE_TABLE=("|+1| compile |60||passed|") + run githubactions_finalreport + [ "${status}" -eq 0 ] + grep -q "Build completed successfully" "${GITHUB_STEP_SUMMARY}" +} + +@test "githubactions_finalreport (with failed tests)" { + setup_gha + RESULT=1 + TP_VOTE_TABLE=("|-1| unit |120||tests failed|") + TP_TEST_TABLE=("| Failed tests | org.example.TestFoo |") + run githubactions_finalreport + [ "${status}" -eq 0 ] + grep -q "Failed Tests" "${GITHUB_STEP_SUMMARY}" + grep -q "TestFoo" "${GITHUB_STEP_SUMMARY}" +} + +@test "githubactions_finalreport (vote table header row)" { + setup_gha + RESULT=0 + TP_VOTE_TABLE=("|H||||Prechecks|") + run githubactions_finalreport + [ "${status}" -eq 0 ] + grep -q "_Prechecks_" "${GITHUB_STEP_SUMMARY}" +} + +@test "githubactions_finalreport (emoji vote enabled)" { + setup_gha + RESULT=0 + GITHUB_USE_EMOJI_VOTE=true + TP_VOTE_TABLE=("|+1| compile |60||passed|") + run githubactions_finalreport + [ "${status}" -eq 0 ] + grep -q ":green_heart:" "${GITHUB_STEP_SUMMARY}" +} + +@test "githubactions_finalreport (emoji vote disabled)" { + setup_gha + RESULT=0 + GITHUB_USE_EMOJI_VOTE=false + TP_VOTE_TABLE=("|+1| compile |60||passed|") + run githubactions_finalreport + [ "${status}" -eq 0 ] + # Should have +1 but not the emoji + grep -q "+1" "${GITHUB_STEP_SUMMARY}" + ! grep -q ":green_heart:" "${GITHUB_STEP_SUMMARY}" +} From e81845bcd4301451143d0942aef8ad3eca011b53 Mon Sep 17 00:00:00 2001 From: Nick Dimiduk Date: Mon, 2 Feb 2026 12:05:08 +0100 Subject: [PATCH 2/2] add docker support --- precommit/src/main/shell/core.d/docker.sh | 6 ++- .../src/main/shell/robots.d/githubactions.sh | 19 ++++++++ .../test/shell/githubactions_finalreport.bats | 44 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/precommit/src/main/shell/core.d/docker.sh b/precommit/src/main/shell/core.d/docker.sh index fa518e94e..3a5581c7e 100755 --- a/precommit/src/main/shell/core.d/docker.sh +++ b/precommit/src/main/shell/core.d/docker.sh @@ -939,9 +939,13 @@ function docker_handler determine_user - # need to call this explicitly + # need to call these explicitly (not in plugin lists) console_docker_support + if declare -f githubactions_docker_support >/dev/null; then + githubactions_docker_support + fi + for plugin in ${PROJECT_NAME} ${BUILDTOOL} "${BUGSYSTEMS[@]}" "${TESTTYPES[@]}" "${TESTFORMATS[@]}"; do if declare -f "${plugin}_docker_support" >/dev/null; then "${plugin}_docker_support" diff --git a/precommit/src/main/shell/robots.d/githubactions.sh b/precommit/src/main/shell/robots.d/githubactions.sh index 75248ff62..d614e07b3 100755 --- a/precommit/src/main/shell/robots.d/githubactions.sh +++ b/precommit/src/main/shell/robots.d/githubactions.sh @@ -85,6 +85,25 @@ function githubactions_set_plugin_defaults GITHUB_REPO="${GITHUB_REPOSITORY}" } +## @description Docker support for GitHub Actions +## @audience private +## @stability evolving +## @replaceable no +function githubactions_docker_support +{ + if [[ -z "${GITHUB_STEP_SUMMARY}" ]]; then + return 0 + fi + + if [[ ! -f "${GITHUB_STEP_SUMMARY}" ]]; then + return 0 + fi + + DOCKER_EXTRAARGS+=("-v" "${GITHUB_STEP_SUMMARY}:${DOCKER_WORK_DIR}/step_summary.md") + GITHUB_STEP_SUMMARY="${DOCKER_WORK_DIR}/step_summary.md" + add_docker_env GITHUB_STEP_SUMMARY +} + function githubactions_cleanup_and_exit { echo "::endgroup::" diff --git a/precommit/src/test/shell/githubactions_finalreport.bats b/precommit/src/test/shell/githubactions_finalreport.bats index df3ee079e..cd4e003d2 100644 --- a/precommit/src/test/shell/githubactions_finalreport.bats +++ b/precommit/src/test/shell/githubactions_finalreport.bats @@ -129,3 +129,47 @@ setup_gha() { grep -q "+1" "${GITHUB_STEP_SUMMARY}" ! grep -q ":green_heart:" "${GITHUB_STEP_SUMMARY}" } + +setup_docker_support() { + # Source the githubactions robot + # shellcheck disable=SC1090 + . "${BATS_TEST_DIRNAME}/../../main/shell/robots.d/githubactions.sh" + + # Mock add_docker_env + DOCKER_EXTRAENVS=() + add_docker_env() { + for k in "$@"; do + DOCKER_EXTRAENVS+=("${k}") + done + } + + DOCKER_EXTRAARGS=() + DOCKER_WORK_DIR="/workdir" +} + +@test "githubactions_docker_support (no GITHUB_STEP_SUMMARY)" { + setup_docker_support + unset GITHUB_STEP_SUMMARY + run githubactions_docker_support + [ "${status}" -eq 0 ] + [ "${#DOCKER_EXTRAARGS[@]}" -eq 0 ] +} + +@test "githubactions_docker_support (GITHUB_STEP_SUMMARY file missing)" { + setup_docker_support + GITHUB_STEP_SUMMARY="/nonexistent/path/summary.md" + run githubactions_docker_support + [ "${status}" -eq 0 ] + [ "${#DOCKER_EXTRAARGS[@]}" -eq 0 ] +} + +@test "githubactions_docker_support (mounts summary file)" { + setup_docker_support + GITHUB_STEP_SUMMARY="${TMP}/step_summary.md" + touch "${GITHUB_STEP_SUMMARY}" + githubactions_docker_support + [[ "${DOCKER_EXTRAARGS[*]}" == *"-v"* ]] + [[ "${DOCKER_EXTRAARGS[*]}" == *"${TMP}/step_summary.md:/workdir/step_summary.md"* ]] + [ "${GITHUB_STEP_SUMMARY}" = "/workdir/step_summary.md" ] + [[ "${DOCKER_EXTRAENVS[*]}" == *"GITHUB_STEP_SUMMARY"* ]] +}