From 523fda35cdddf4679c6e5428e539f835ba3d1c93 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Thu, 15 Oct 2026 12:21:27 +0200 Subject: [PATCH 1/2] feat: add per-severity counts to the code issues badge JSON codecheckToolbox now writes the error, warning and info counts to a "counts" field in code_issues.json, so matbox-actions can draw one badge segment per severity. The label, message and color fields are unchanged, so renderers that ignore the counts still draw the current single badge. writeBadgeJSONFile and the internal createBadge take an optional Counts name-value argument. The field is omitted when Counts is empty, which keeps every other badge JSON unchanged. The legacy pybadges SVG fallback ignores the counts. Co-Authored-By: Claude Opus 5.5 --- code/+matbox/+tasks/+internal/createBadge.m | 10 ++++++++-- code/+matbox/+tasks/codecheckToolbox.m | 10 +++++++++- code/+matbox/+utility/writeBadgeJSONFile.m | 8 ++++++++ tools/tests/+matboxtools/+unittest/TasksTest.m | 17 +++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/code/+matbox/+tasks/+internal/createBadge.m b/code/+matbox/+tasks/+internal/createBadge.m index 60a1c01..83af5f6 100644 --- a/code/+matbox/+tasks/+internal/createBadge.m +++ b/code/+matbox/+tasks/+internal/createBadge.m @@ -1,4 +1,4 @@ -function createBadge(label, message, color, projectRootDirectory) +function createBadge(label, message, color, projectRootDirectory, options) % createBadge - Write a badge as JSON or as a legacy SVG file % % Writes a badge description as a JSON file (docs/reports/badges) when @@ -10,17 +10,23 @@ function createBadge(label, message, color, projectRootDirectory) % pybadges Python package, for callers without a render step: local runs % and repositories pinned to older matbox-actions versions. This % fallback will be removed in a future major release. +% +% Optional name/value arguments: +% Counts - Scalar struct of named counts added to the badge JSON for +% segmented rendering. The legacy SVG fallback ignores it. arguments label (1,1) string message (1,1) string color (1,1) string projectRootDirectory (1,1) string {mustBeFolder} + options.Counts struct {mustBeScalarOrEmpty} = struct.empty end if strcmpi(getenv("MATBOX_BADGE_FORMAT"), "json") matbox.utility.writeBadgeJSONFile(label, message, color, ... - "OutputFolder", fullfile(projectRootDirectory, "docs", "reports", "badges")) + "OutputFolder", fullfile(projectRootDirectory, "docs", "reports", "badges"), ... + "Counts", options.Counts) else matbox.utility.createBadgeSvg(label, message, color, projectRootDirectory) end diff --git a/code/+matbox/+tasks/codecheckToolbox.m b/code/+matbox/+tasks/codecheckToolbox.m index 1fc1dac..d7d4918 100644 --- a/code/+matbox/+tasks/codecheckToolbox.m +++ b/code/+matbox/+tasks/codecheckToolbox.m @@ -93,8 +93,16 @@ function createCodeIssuesBadge(issueCount, projectRootDir) color = "red"; end + % The counts let matbox-actions draw one segment per severity. The + % label, message and color still describe a complete single badge + % for renderers that ignore the counts. + counts = struct( ... + "error", issueCount.Error, ... + "warning", issueCount.Warning, ... + "info", issueCount.Info); + matbox.tasks.internal.createBadge("code issues", ... - string(issueCount.Total), color, projectRootDir) + string(issueCount.Total), color, projectRootDir, "Counts", counts) end function ME = throwUnresolvedCodeIssuesException() diff --git a/code/+matbox/+utility/writeBadgeJSONFile.m b/code/+matbox/+utility/writeBadgeJSONFile.m index b5189b0..1658f2c 100644 --- a/code/+matbox/+utility/writeBadgeJSONFile.m +++ b/code/+matbox/+utility/writeBadgeJSONFile.m @@ -24,6 +24,10 @@ function writeBadgeJSONFile(label, message, color, projectRootDirectory, options % % FileName - Name of file excluding extension % +% Counts - Scalar struct of named counts written to a "counts" field, +% for renderers that draw one segment per count. The field +% is omitted when Counts is empty (default). +% % Example: % % writeBadgeJSONFile("Test Coverage", "90%", "green") creates a file @@ -44,6 +48,7 @@ function writeBadgeJSONFile(label, message, color, projectRootDirectory, options projectRootDirectory (1,1) string = missing options.OutputFolder (1,1) string = missing options.FileName (1,1) string = missing + options.Counts struct {mustBeScalarOrEmpty} = struct.empty end if ismissing(options.OutputFolder) @@ -62,6 +67,9 @@ function writeBadgeJSONFile(label, message, color, projectRootDirectory, options badgeInfo.label = label; badgeInfo.message = message; badgeInfo.color = color; + if ~isempty(options.Counts) + badgeInfo.counts = options.Counts; + end badgeJSON = jsonencode(badgeInfo); if ismissing(options.FileName) diff --git a/tools/tests/+matboxtools/+unittest/TasksTest.m b/tools/tests/+matboxtools/+unittest/TasksTest.m index a6eedb9..d0d795b 100644 --- a/tools/tests/+matboxtools/+unittest/TasksTest.m +++ b/tools/tests/+matboxtools/+unittest/TasksTest.m @@ -55,6 +55,23 @@ function testCodecheckToolboxCreatesBadgeJson(testCase) testCase.verifyEqual(badgeInfo.label, 'code issues') testCase.verifyTrue(ismember(string(badgeInfo.color), ... ["green", "yellow", "red"])) + + % The per-severity counts must add up to the total in the message + testCase.assertTrue(isfield(badgeInfo, "counts"), ... + "Expected badge JSON to contain a counts field.") + counts = badgeInfo.counts; + testCase.verifyEqual(sort(string(fieldnames(counts))), ... + ["error"; "info"; "warning"]) + testCase.verifyEqual(counts.error + counts.warning + counts.info, ... + str2double(badgeInfo.message)) + end + + function testWriteBadgeJSONFileOmitsCountsByDefault(testCase) + matbox.utility.writeBadgeJSONFile("tests", "1 passed", "green", ... + "OutputFolder", pwd) + + badgeInfo = jsondecode(fileread(fullfile(pwd, "tests.json"))); + testCase.verifyFalse(isfield(badgeInfo, "counts")) end function testPackageToolbox(testCase) From cd79d4deb4cdc29e0ccbb6e81f5f1b09b2df03aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Oct 2026 19:23:25 +0000 Subject: [PATCH 2/2] Update GitHub badges [skip ci] --- .github/badges/tests.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/badges/tests.svg b/.github/badges/tests.svg index 7afe4fa..7199f22 100644 --- a/.github/badges/tests.svg +++ b/.github/badges/tests.svg @@ -1 +1 @@ -tests: 36 passedtests36 passed +tests: 37 passedtests37 passed