Skip to content

Commit 7127e21

Browse files
committed
Merge job summary output with matrix creation job
Spinning up runners is expensive (time-wise) so reuse the same runner. Still use the outputs to ensure it shows what is actually used later.
1 parent ccdf0eb commit 7127e21

1 file changed

Lines changed: 176 additions & 181 deletions

File tree

.github/workflows/reusable.yml

Lines changed: 176 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,182 @@ jobs:
512512
MIN_CMAKE_VERSION: ${{ inputs.min_cmake_version }}
513513
MAX_CXX_STDS_PER_JOB: ${{ inputs.max_cxx_stds_per_job }}
514514

515+
- name: Determine library name
516+
run: |
517+
set -x
518+
if [[ -z "$SELF" ]]; then
519+
mkdir meta
520+
wget -q -O meta/libraries.json "https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/${GITHUB_SHA}/meta/libraries.json" && \
521+
wget -q -O get_libname.py "https://raw.githubusercontent.com/boostorg/boost-ci/${WORKFLOW_SHA}/ci/get_libname.py" && \
522+
SELF=$(BOOST_CI_SRC_FOLDER="$PWD" python3 get_libname.py) || SELF='(failed to extract)'
523+
fi
524+
echo "SELF=$SELF" >> "$GITHUB_ENV"
525+
env:
526+
SELF: ${{inputs.library_folder}}
527+
WORKFLOW_SHA: ${{job.workflow_sha}}
528+
529+
- name: Generate CI summary
530+
run: |
531+
import json
532+
import os
533+
import re
534+
535+
def merge_cxxstd_duplicates(entries):
536+
"""Merge consecutive entries identical except for 'cxxstd', concatenating cxxstd.
537+
Works because jobs split by C++ std are emitted consecutively by the matrix generator."""
538+
merged = []
539+
for e in entries:
540+
if merged:
541+
prev = merged[-1]
542+
if {k: v for k, v in prev.items() if k != 'cxxstd'} == {k: v for k, v in e.items() if k != 'cxxstd'}:
543+
a, b = prev.get('cxxstd', ''), e.get('cxxstd', '')
544+
prev['cxxstd'] = f"{a},{b}" if a and b else (a or b)
545+
continue
546+
merged.append(e)
547+
return merged
548+
def load_from_env(name):
549+
value = os.environ.get(name)
550+
return merge_cxxstd_duplicates(json.loads(value)['include']) if value else []
551+
def sort_key(entry):
552+
# Natural sort for compiler
553+
compiler = tuple(int(p) if p.isdigit() else p
554+
for p in re.findall(r'\D+|\d+', entry.get('compiler', entry.get('toolset')))
555+
)
556+
return (compiler, entry.get('name') or ' ') # Named entries after non-named with same compiler
557+
def compiler_name(toolset):
558+
result = toolset.replace('-', ' ').upper()
559+
result = result.replace('CLANG', 'Clang')
560+
result = result.replace('ICPX', 'Intel OneAPI')
561+
return {'msvc-14.3': 'Visual Studio 2022',
562+
'msvc-14.5': 'Visual Studio 2026',
563+
'clang-win': 'Clang',
564+
}.get(toolset, result)
565+
566+
summary = "# Tested Configurations\n\n"
567+
568+
windows_entries = load_from_env('WINDOWS_MATRIX_ENTRIES')
569+
mingw_entries = load_from_env('MINGW_MATRIX_ENTRIES')
570+
cmake_entries = load_from_env('CMAKE_MATRIX_ENTRIES')
571+
posix_entries = load_from_env('POSIX_MATRIX_ENTRIES')
572+
linux_entries = [e for e in posix_entries if 'ubuntu' in e.get('os', '') or 'ubuntu' in e.get('container', '')]
573+
macos_entries = [e for e in posix_entries if 'macos' in e.get('os', '')]
574+
575+
# B2 build options (apply to all B2 jobs)
576+
if posix_entries or windows_entries or mingw_entries:
577+
options = []
578+
library_folder = os.environ.get('LIBRARY_FOLDER')
579+
if not library_folder:
580+
library_folder = os.environ.get('SELF') + " (default)"
581+
options.append(("Library folder", library_folder))
582+
options.append(("Variant", os.environ.get('B2_VARIANT') or '[default]'))
583+
options.append(("Link", os.environ.get('B2_LINK') or '[default]'))
584+
defines = os.environ.get('B2_DEFINES')
585+
if defines:
586+
options.append(("Defines", defines))
587+
targets = os.environ.get('B2_TARGETS')
588+
if targets:
589+
options.append(("Targets", targets))
590+
b2_headers_flags = os.environ.get('B2_HEADERS_FLAGS')
591+
if b2_headers_flags:
592+
options.append(("Extra flags for `b2 headers`", b2_headers_flags))
593+
parallelism = os.environ.get('B2_JOBS')
594+
if parallelism:
595+
options.append(("Parallelism (`-j`)", parallelism))
596+
summary += "## B2 Build Options\n\n"
597+
summary += "| Option | Value |\n"
598+
summary += "|--------|-------|\n"
599+
for name, value in options:
600+
summary += f"| {name} | {value} |\n"
601+
summary += "\n"
602+
603+
if linux_entries:
604+
summary += "## Linux\n\n"
605+
summary += "| Job | Compiler | C++ std | Arch |\n"
606+
summary += "|-----|----------|---------|------|\n"
607+
for e in sorted(linux_entries, key=sort_key):
608+
compiler = e['compiler']
609+
stdlib = e.get('stdlib', '')
610+
name = e.get('name')
611+
if name:
612+
if stdlib:
613+
compiler += f" ({stdlib})"
614+
else:
615+
name = compiler_name(compiler)
616+
if stdlib:
617+
name += f" ({stdlib})"
618+
cxxstd = e.get('cxxstd', '[default]')
619+
arch = e.get('address-model', '')
620+
summary += f"| {name} | {compiler} | {cxxstd} | {arch} |\n"
621+
summary += "\n"
622+
623+
if macos_entries:
624+
summary += "## macOS\n\n"
625+
summary += "| Job | Compiler | C++ std | OS |\n"
626+
summary += "|-----|----------|---------|----|\n"
627+
for e in sorted(macos_entries, key=sort_key):
628+
compiler = e['compiler']
629+
name = e.get('name', compiler.replace('-', ' ').capitalize())
630+
cxxstd = e.get('cxxstd', '[default]')
631+
summary += f"| {name} | {compiler} | {cxxstd} | {e['os'].replace('-', ' ').upper()} |\n"
632+
summary += "\n"
633+
634+
if windows_entries or mingw_entries:
635+
summary += "## Windows\n\n"
636+
summary += "| Job | Toolset | C++ std | Arch |\n"
637+
summary += "|-----|---------|---------|------|\n"
638+
for e in sorted(windows_entries, key=sort_key):
639+
toolset = e['toolset']
640+
cxxstd = e.get('cxxstd', '[default]')
641+
arch = e.get('address-model', '')
642+
name = e.get('name')
643+
if not name:
644+
if e.get('target-os') == 'cygwin':
645+
name = 'Cygwin'
646+
else:
647+
name = ''
648+
name += ' ' + compiler_name(toolset)
649+
if 'arm' in e['os'] and 'arm' not in name.lower():
650+
name = 'ARM: ' + name
651+
summary += f"| {name} | {toolset} | {cxxstd} | {arch} |\n"
652+
653+
if mingw_entries:
654+
for e in mingw_entries:
655+
sys = e['sys']
656+
compiler = e['compiler']
657+
cxxstd = e.get('cxxstd', '[default]')
658+
arch = e.get('address-model', sys.upper().replace('MINGW', '')) # Default: MINGW64 -> 64
659+
summary += f"| {sys} | {compiler} | {cxxstd} | {arch} |\n"
660+
summary += "\n"
661+
662+
if cmake_entries:
663+
summary += "## CMake\n\n"
664+
summary += "| Generator | Build Type | Shared | OS | |\n"
665+
summary += "|-----------|------------|--------|----|-|\n"
666+
667+
for c in cmake_entries:
668+
summary += f"| {c['generator']} | {c['build_type']} | {c['build_shared']} | {c['os']} |"
669+
cmake_version = c.get('cmake_version')
670+
if cmake_version:
671+
summary += f" CMake {cmake_version} "
672+
summary += "|\n"
673+
674+
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as fh:
675+
fh.write(summary)
676+
shell: python
677+
env:
678+
POSIX_MATRIX_ENTRIES: ${{steps.generate-matrix.outputs.posix-matrix}}
679+
WINDOWS_MATRIX_ENTRIES: ${{steps.generate-matrix.outputs.windows-matrix}}
680+
MINGW_MATRIX_ENTRIES: ${{steps.generate-matrix.outputs.mingw-matrix}}
681+
CMAKE_MATRIX_ENTRIES: ${{steps.generate-matrix.outputs.cmake-matrix}}
682+
MIN_CMAKE_VERSION: ${{inputs.min_cmake_version}}
683+
B2_JOBS: ${{inputs.parallelism}}
684+
B2_HEADERS_FLAGS: ${{inputs.b2_headers_flags}}
685+
B2_VARIANT: ${{inputs.b2_variant}}
686+
B2_LINK: ${{inputs.b2_link}}
687+
B2_DEFINES: ${{inputs.b2_defines}}
688+
B2_TARGETS: ${{inputs.b2_targets}}
689+
LIBRARY_FOLDER: ${{inputs.library_folder}}
690+
515691
posix:
516692
if: ${{needs.generate-job-matrix.outputs.posix-matrix}}
517693
needs: generate-job-matrix
@@ -1060,184 +1236,3 @@ jobs:
10601236
- name: Run CMake install tests w/ B2 installed library
10611237
run: ci/test_installed_cmake_config.sh
10621238
env: {BOOST_CI_INSTALLED_BY: B2}
1063-
1064-
ci-summary:
1065-
needs: generate-job-matrix
1066-
runs-on: ubuntu-latest
1067-
if: always()
1068-
steps:
1069-
- name: Determine library name
1070-
run: |
1071-
set -x
1072-
if [[ -z "$SELF" ]]; then
1073-
mkdir meta
1074-
wget -q -O meta/libraries.json "https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/${GITHUB_SHA}/meta/libraries.json" && \
1075-
wget -q -O get_libname.py "https://raw.githubusercontent.com/boostorg/boost-ci/${WORKFLOW_SHA}/ci/get_libname.py" && \
1076-
SELF=$(BOOST_CI_SRC_FOLDER="$PWD" python3 get_libname.py) || SELF='(failed to extract)'
1077-
fi
1078-
echo "SELF=$SELF" >> "$GITHUB_ENV"
1079-
env:
1080-
SELF: ${{inputs.library_folder}}
1081-
WORKFLOW_SHA: ${{job.workflow_sha}}
1082-
1083-
- name: Generate CI summary
1084-
run: |
1085-
import json
1086-
import os
1087-
import re
1088-
1089-
def merge_cxxstd_duplicates(entries):
1090-
"""Merge consecutive entries identical except for 'cxxstd', concatenating cxxstd.
1091-
Works because jobs split by C++ std are emitted consecutively by the matrix generator."""
1092-
merged = []
1093-
for e in entries:
1094-
if merged:
1095-
prev = merged[-1]
1096-
if {k: v for k, v in prev.items() if k != 'cxxstd'} == {k: v for k, v in e.items() if k != 'cxxstd'}:
1097-
a, b = prev.get('cxxstd', ''), e.get('cxxstd', '')
1098-
prev['cxxstd'] = f"{a},{b}" if a and b else (a or b)
1099-
continue
1100-
merged.append(e)
1101-
return merged
1102-
def load_from_env(name):
1103-
value = os.environ.get(name)
1104-
return merge_cxxstd_duplicates(json.loads(value)['include']) if value else []
1105-
def sort_key(entry):
1106-
# Natural sort for compiler
1107-
compiler = tuple(int(p) if p.isdigit() else p
1108-
for p in re.findall(r'\D+|\d+', entry.get('compiler', entry.get('toolset')))
1109-
)
1110-
return (compiler, entry.get('name') or ' ') # Named entries after non-named with same compiler
1111-
def compiler_name(toolset):
1112-
result = toolset.replace('-', ' ').upper()
1113-
result = result.replace('CLANG', 'Clang')
1114-
result = result.replace('ICPX', 'Intel OneAPI')
1115-
return {'msvc-14.3': 'Visual Studio 2022',
1116-
'msvc-14.5': 'Visual Studio 2026',
1117-
'clang-win': 'Clang',
1118-
}.get(toolset, result)
1119-
1120-
summary = "# Tested Configurations\n\n"
1121-
1122-
windows_entries = load_from_env('WINDOWS_MATRIX_ENTRIES')
1123-
mingw_entries = load_from_env('MINGW_MATRIX_ENTRIES')
1124-
cmake_entries = load_from_env('CMAKE_MATRIX_ENTRIES')
1125-
posix_entries = load_from_env('POSIX_MATRIX_ENTRIES')
1126-
linux_entries = [e for e in posix_entries if 'ubuntu' in e.get('os', '') or 'ubuntu' in e.get('container', '')]
1127-
macos_entries = [e for e in posix_entries if 'macos' in e.get('os', '')]
1128-
1129-
# B2 build options (apply to all B2 jobs)
1130-
if posix_entries or windows_entries or mingw_entries:
1131-
options = []
1132-
library_folder = os.environ.get('LIBRARY_FOLDER')
1133-
if not library_folder:
1134-
library_folder = os.environ.get('SELF') + " (default)"
1135-
options.append(("Library folder", library_folder))
1136-
options.append(("Variant", os.environ.get('B2_VARIANT') or '[default]'))
1137-
options.append(("Link", os.environ.get('B2_LINK') or '[default]'))
1138-
defines = os.environ.get('B2_DEFINES')
1139-
if defines:
1140-
options.append(("Defines", defines))
1141-
targets = os.environ.get('B2_TARGETS')
1142-
if targets:
1143-
options.append(("Targets", targets))
1144-
b2_headers_flags = os.environ.get('B2_HEADERS_FLAGS')
1145-
if b2_headers_flags:
1146-
options.append(("Extra flags for `b2 headers`", b2_headers_flags))
1147-
parallelism = os.environ.get('B2_JOBS')
1148-
if parallelism:
1149-
options.append(("Parallelism (`-j`)", parallelism))
1150-
summary += "## B2 Build Options\n\n"
1151-
summary += "| Option | Value |\n"
1152-
summary += "|--------|-------|\n"
1153-
for name, value in options:
1154-
summary += f"| {name} | {value} |\n"
1155-
summary += "\n"
1156-
1157-
if linux_entries:
1158-
summary += "## Linux\n\n"
1159-
summary += "| Job | Compiler | C++ std | Arch |\n"
1160-
summary += "|-----|----------|---------|------|\n"
1161-
for e in sorted(linux_entries, key=sort_key):
1162-
compiler = e['compiler']
1163-
stdlib = e.get('stdlib', '')
1164-
name = e.get('name')
1165-
if name:
1166-
if stdlib:
1167-
compiler += f" ({stdlib})"
1168-
else:
1169-
name = compiler_name(compiler)
1170-
if stdlib:
1171-
name += f" ({stdlib})"
1172-
cxxstd = e.get('cxxstd', '[default]')
1173-
arch = e.get('address-model', '')
1174-
summary += f"| {name} | {compiler} | {cxxstd} | {arch} |\n"
1175-
summary += "\n"
1176-
1177-
if macos_entries:
1178-
summary += "## macOS\n\n"
1179-
summary += "| Job | Compiler | C++ std | OS |\n"
1180-
summary += "|-----|----------|---------|----|\n"
1181-
for e in sorted(macos_entries, key=sort_key):
1182-
compiler = e['compiler']
1183-
name = e.get('name', compiler.replace('-', ' ').capitalize())
1184-
cxxstd = e.get('cxxstd', '[default]')
1185-
summary += f"| {name} | {compiler} | {cxxstd} | {e['os'].replace('-', ' ').upper()} |\n"
1186-
summary += "\n"
1187-
1188-
if windows_entries or mingw_entries:
1189-
summary += "## Windows\n\n"
1190-
summary += "| Job | Toolset | C++ std | Arch |\n"
1191-
summary += "|-----|---------|---------|------|\n"
1192-
for e in sorted(windows_entries, key=sort_key):
1193-
toolset = e['toolset']
1194-
cxxstd = e.get('cxxstd', '[default]')
1195-
arch = e.get('address-model', '')
1196-
name = e.get('name')
1197-
if not name:
1198-
if e.get('target-os') == 'cygwin':
1199-
name = 'Cygwin'
1200-
else:
1201-
name = ''
1202-
name += ' ' + compiler_name(toolset)
1203-
if 'arm' in e['os'] and 'arm' not in name.lower():
1204-
name = 'ARM: ' + name
1205-
summary += f"| {name} | {toolset} | {cxxstd} | {arch} |\n"
1206-
1207-
if mingw_entries:
1208-
for e in mingw_entries:
1209-
sys = e['sys']
1210-
compiler = e['compiler']
1211-
cxxstd = e.get('cxxstd', '[default]')
1212-
arch = e.get('address-model', sys.upper().replace('MINGW', '')) # Default: MINGW64 -> 64
1213-
summary += f"| {sys} | {compiler} | {cxxstd} | {arch} |\n"
1214-
summary += "\n"
1215-
1216-
if cmake_entries:
1217-
summary += "## CMake\n\n"
1218-
summary += "| Generator | Build Type | Shared | OS | |\n"
1219-
summary += "|-----------|------------|--------|----|-|\n"
1220-
1221-
for c in cmake_entries:
1222-
summary += f"| {c['generator']} | {c['build_type']} | {c['build_shared']} | {c['os']} |"
1223-
cmake_version = c.get('cmake_version')
1224-
if cmake_version:
1225-
summary += f" CMake {cmake_version} "
1226-
summary += "|\n"
1227-
1228-
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as fh:
1229-
fh.write(summary)
1230-
shell: python
1231-
env:
1232-
POSIX_MATRIX_ENTRIES: ${{needs.generate-job-matrix.outputs.posix-matrix}}
1233-
WINDOWS_MATRIX_ENTRIES: ${{needs.generate-job-matrix.outputs.windows-matrix}}
1234-
MINGW_MATRIX_ENTRIES: ${{needs.generate-job-matrix.outputs.mingw-matrix}}
1235-
CMAKE_MATRIX_ENTRIES: ${{needs.generate-job-matrix.outputs.cmake-matrix}}
1236-
MIN_CMAKE_VERSION: ${{inputs.min_cmake_version}}
1237-
B2_JOBS: ${{inputs.parallelism}}
1238-
B2_HEADERS_FLAGS: ${{inputs.b2_headers_flags}}
1239-
B2_VARIANT: ${{inputs.b2_variant}}
1240-
B2_LINK: ${{inputs.b2_link}}
1241-
B2_DEFINES: ${{inputs.b2_defines}}
1242-
B2_TARGETS: ${{inputs.b2_targets}}
1243-
LIBRARY_FOLDER: ${{inputs.library_folder}}

0 commit comments

Comments
 (0)